diff --git a/.github/.release.yml b/.github/.release.yml new file mode 100644 index 0000000..7dd4ab9 --- /dev/null +++ b/.github/.release.yml @@ -0,0 +1,26 @@ +# .github/release.yml + +changelog: + categories: + - title: ๐Ÿ• Features + labels: + - "*" + exclude: + labels: + - dependencies + - chore + - docs + - refactor + - style + - test + - fix + - title: ๐Ÿ› Bug Fixes + labels: + - bug + - fix + - title: ๐Ÿ“š Documentation + labels: + - docs + - title: ๐Ÿ”„ Refactoring + labels: + - refactor diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..0633df5 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: Run Tests + +on: + pull_request: + branches: [ main, develop ] + push: + branches: [ main, develop ] + +jobs: + test: + name: Run Tests and Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.23.0' + cache: true + + - name: Install dependencies + run: go mod download + + - name: Run Tests + run: | + make test diff --git a/.golangci.yml b/.golangci.yml index 6f5db54..7f2dd96 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -12,7 +12,7 @@ linters: - ineffassign - typecheck -issues: +issues: exclude-use-default: false max-same-issues: 5 max-issues-per-linter: 0 diff --git a/Makefile b/Makefile index c78c2ec..6766214 100644 --- a/Makefile +++ b/Makefile @@ -83,3 +83,20 @@ swagger-3-0: install-swag swagger-fix-refs: @./scripts/fix_swagger_refs.sh + + + +.PHONY: test test-verbose test-coverage + +# Run all tests +test: + go test -v -race ./internal/... + +# Run tests with verbose output +test-verbose: + go test -v ./internal/... + +# Run tests with coverage report +test-coverage: + go test -coverprofile=coverage.out ./internal/... + go tool cover -html=coverage.out -o coverage.html diff --git a/README.md b/README.md index 4dd7f17..e46e937 100644 --- a/README.md +++ b/README.md @@ -1,370 +1,623 @@ -# E-commerce Backend Service +# CodeGeeky - Open Source LMS Platform -A Go-based e-commerce backend service built using clean architecture principles. + -## Table of Contents +

+ +๐ŸŽ“ Modern Learning Management System for Developers ๐ŸŽ“ + +

-- [Project Structure](#project-structure) -- [Getting Started](#getting-started) -- [Development Guide](#development-guide) -- [Error Handling](#error-handling) -- [Adding New Features](#adding-new-features) -- [Best Practices](#best-practices) +

+Build feature-rich learning platforms with internship management, payment processing, and advanced analytics. CodeGeeky handles enrollment workflows, payment integration, and content management so you can focus on building, not infrastructure. +

-## Project Structure +
-``` -โ”œโ”€โ”€ internal/ -โ”‚ โ”œโ”€โ”€ api/ # HTTP handlers and middleware -โ”‚ โ”‚ โ”œโ”€โ”€ middleware/ # HTTP middleware components -โ”‚ โ”‚ โ”œโ”€โ”€ router/ # Router setup and configuration -โ”‚ โ”‚ โ””โ”€โ”€ v1/ # API version 1 handlers -โ”‚ โ”œโ”€โ”€ bootstrap/ # Application bootstrapping -โ”‚ โ”œโ”€โ”€ config/ # Configuration management -โ”‚ โ”œโ”€โ”€ domain/ # Business domain models and interfaces -โ”‚ โ”œโ”€โ”€ pkg/ # Shared utilities -โ”‚ โ”œโ”€โ”€ repository/ # Data access implementations -โ”‚ โ”œโ”€โ”€ service/ # Business logic implementation -โ”‚ โ””โ”€โ”€ validator/ # Request validation -``` +[Documentation](https://docs.codegeeky.io) โ€ข [Demo](https://demo.codegeeky.io) โ€ข [Website](https://codegeeky.io/) โ€ข [LinkedIn](https://www.linkedin.com/company/codegeeky) + +[![Go](https://img.shields.io/badge/go-%2300ADD8.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/omkar273/codegeeky) [![PostgreSQL](https://img.shields.io/badge/postgresql-%23316192.svg?style=for-the-badge&logo=postgresql&logoColor=white)](https://www.postgresql.org/) [![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://www.docker.com/) + +
+ +--- + +## Open Architecture + +The CodeGeeky core (enrollment, payments, content management, analytics) has an open and composable design. + + + +Your application, whether it's running educational platforms, corporate training systems, or custom learning workflows, can integrate with CodeGeeky. You can directly stream data from existing LMS systems, analytics pipelines, or build custom integrations. + +At the core, CodeGeeky processes this data in real time. We handle everything that usually ends up as custom logic built by developers. Our platform manages enrollment workflows, processes payments through multiple gateways, applies discounts and promotions, enforces access controls, and generates comprehensive analytics automatically. Whether you're using subscription-based learning, pay-per-course models, or enterprise training programs, you can set up and iterate on your learning platform without writing LMS infrastructure from scratch. + +After enrollment and payment processing, our platform connects to your existing tools for payments, CRM, analytics, and content management, ensuring learning data flows into the systems your business already uses. It can sync enrollment data to your CRM, update payment information in your payment processor, and push learning analytics to your business intelligence tools. + +With this architecture, you get full control over how your learning platform works, while saving your team from the complexity of maintaining it all. + +## Why LMS development is a developer problem? + +##### TL;DR + +_When existing LMS tools don't flex to your product's needs, developers shoulder the burden eating up valuable development time and causing ongoing maintenance headaches._ + +

+ struggle is real +

+ +Modern learning platform developers often find themselves wrestling with LMS systems. Here are some of the biggest pain points that turn LMS development into a "developer problem": + +- **Rigid LMS platforms (lack of flexibility):** Traditional LMS services handle basic course management or simple enrollments, but anything beyond that โ€“ internship programs, payment processing, advanced analytics, custom enrollment workflows, feature gating, custom reporting logic โ€“ usually isn't supported out of the box. Developers end up writing countless workarounds or custom code to accommodate these needs. In other words, if your learning model doesn't fit the tool, you're stuck bending your product or building logic from scratch. + +- **Complex enrollment and payment integration at scale:** Implementing accurate enrollment tracking with payment processing is hard. It involves capturing high-volume enrollment events, processing payments through multiple gateways, handling edge cases (refunds, partial enrollments, time zones, etc.), and ensuring it all works reliably at scale. Few teams anticipate how many moving parts this requires until they're deep in the weeds of building it. + +- **Vendor lock-in and black boxes:** Relying on third-party LMS platforms can mean surrendering flexibility. Many SaaS LMS providers are closed systems or charge a percentage fee on revenue, which frustrates engineers who want full control over their learning logic and data. Changing providers later can be a massive undertaking, so teams feel "stuck" with a less-than-ideal solution. + +- **Delayed monetization & opportunity cost:** Every week spent building or patching an LMS system is a week not spent on core product features. If it takes months to implement a new learning model or payment integration, that's delayed revenue and lost agility for the business. What might be scoped as a "quick two-month project" can quickly spiral into a multi-year maintenance headache, tying up engineering resources and slowing time-to-market for new offerings. + +## How CodeGeeky solves this + +CodeGeeky's approach is to abstract away the hard parts of LMS development while preserving maximum flexibility and transparency for developers. It addresses the above pain points in several key ways: + +- **Developer-first design:** CodeGeeky is built API-first with easy integrations. You can instrument your learning platform by simply sending enrollment events via SDKs, and CodeGeeky handles the enrollment processing, payment integration, and analytics logic in real time. This means minimal code to write on your end and no need to reinvent enrollment workflows or payment processing. + +- **Open-source and self-hostable:** CodeGeeky is open-source, so you can run it on your own infrastructure for full transparency and control. There's no black-box dependency or surprise fees and you're free from vendor lock-in. You can inspect the code, extend functionality, and trust that your learning logic is fully in your hands. + +- **Composable with your stack:** Rather than replacing your existing LMS or payment provider, CodeGeeky augments it. You can build it from scratch or build on top of your existing LMS providers like Moodle or Canvas to manage enrollment data, payment rules, discounts, and entitlements. You can easily integrate with your existing payment gateways, CRM, analytics tools, etc. This layered approach preserves your current workflows and customer touchpoints. + +- **Flexible learning models out-of-the-box:** Whether you need pure internship-based learning, tiered course plans, subscription-based access, prepaid credits, free tiers with premium upgrades, or any hybrid model, CodeGeeky's data model and rules engine can support it. CodeGeeky is designed to accommodate changing learning strategies in minutes that would normally require schema updates and migration scripts. + +- **Transparency and visibility:** Because CodeGeeky tracks every enrollment and ties it to payments, you (and your customers) get clear visibility into learning progress and charges. It can provide real-time enrollment summaries and learning analytics, helping both engineering and business teams ensure everything lines up correctly. Students get detailed progress reports that show exactly what they're learning, reducing confusion and improving engagement. + +By handling the heavy lifting from real-time enrollment tracking to payment processing, CodeGeeky lets your team focus on building your actual learning content, not the LMS system around it. + +## **Features** -## Getting Started +CodeGeeky provides a rich set of features to power modern learning platforms. Key features include: -### Prerequisites +

+ features +

-- Go 1.21 or higher -- MongoDB -- Git +- **[**Internship Management:\*\*](https://docs.codegeeky.io/api-reference/internships) Create and manage comprehensive internship programs with detailed tracking, batch management, and progress monitoring. CodeGeeky's internship system can handle complex workflows and provides real-time analytics, ensuring your internship programs are always up-to-date with actual student progress. -### Setup Steps +- **[**Enrollment & Payment Processing:\*\*](https://docs.codegeeky.io/docs/Enrollment/Overview) Support flexible enrollment workflows with full payment integration. You can process enrollments with multiple payment gateways, apply discounts and promotional codes, and handle complex billing scenarios. CodeGeeky's enrollment system is built-in, so you don't need extra custom logic to handle enrollment workflows or payment processing. + +- **[**Content Management:\*\*](https://docs.codegeeky.io/docs/Content/Overview) Design and iterate on learning content with total flexibility โ€“ whether video courses, interactive assignments, downloadable resources, or hybrid content types. You can launch new content or modify existing content without additional engineering effort. CodeGeeky lets you manage content versions and track usage over time, making it easy to evolve your learning content as your product and market strategy change. + +- **[**Advanced Analytics:\*\*](https://docs.codegeeky.io/docs/Analytics/Overview) Manage learning analytics and business intelligence with comprehensive reporting. CodeGeeky lets you track student progress, enrollment metrics, revenue analytics, and custom KPIs tied to your learning goals. You can enforce learning limits and generate detailed reports without building complex analytics logic yourself. This ensures that your learning platform's analytics are always in sync with what your business needs. + +- **[**Multi-Gateway Payments:\*\*](https://docs.codegeeky.io/docs/Payments/Overview) CodeGeeky generates clear, accurate payment processing through multiple gateways based on real-time enrollment data and payment preferences. It automates payment cycles โ€“ handling refunds, partial payments, and payment failures โ€“ and produces detailed payment reports that give you full visibility into your revenue. Finance teams can easily reconcile payments because every transaction is linked to tracked enrollments or defined pricing. You can also integrate this with your payment processor to automate charging customers once an enrollment is confirmed. + +Each of these features is accessible via CodeGeeky's APIs and dashboard, allowing you to mix and match to build the exact learning platform experience you need. + +## ๐Ÿš€ Quick Start + +### **Prerequisites** + +- **Go 1.24+** (latest stable) +- **PostgreSQL 14+** with PostGIS extension +- **Redis** (for caching and sessions) +- **Docker & Docker Compose** (optional) + +### **Environment Setup** 1. **Clone the Repository** ```bash -git clone https://github.com/your-username/ecommerce.git -cd ecommerce +git clone https://github.com/omkar273/codegeeky.git +cd codegeeky ``` -2. **Environment Setup** - Create a `.env` file in the root directory: - -```env -PORT=8080 -MODE=development -DB_USER=your_db_user -DB_USER_PWD=your_db_password -DB_HOST=your_db_host -DB_NAME=your_db_name -JWT_SECRET=your_jwt_secret -``` - -3. **Install Dependencies** +2. **Install Dependencies** ```bash go mod download ``` -4. **Run the Application** +3. **Configure Environment** + +Copy the configuration template: ```bash -go run cmd/main.go +cp internal/config/config.yaml config.yaml ``` -## Development Guide +Update `config.yaml` with your settings: + +```yaml +# Server Configuration +server: + env: "development" + address: ":8080" + +# Database Configuration +postgres: + host: "localhost" + port: 5432 + user: "codegeeky_user" + password: "your_password" + dbname: "codegeeky_db" + sslmode: "disable" + auto_migrate: true + +# Authentication (Supabase) +supabase: + url: "https://your-project.supabase.co" + key: "your_anon_key" + jwt_secret: "your_jwt_secret" + service_key: "your_service_key" + +# Payment Gateway (Razorpay) +razorpay: + api_key: "rzp_test_your_key" + api_secret: "your_secret" + +# File Storage (Cloudinary) +cloudinary: + api_key: "your_api_key" + api_secret: "your_api_secret" + cloud_name: "your_cloud_name" +``` + +4. **Database Setup** + +```bash +# Create database +createdb codegeeky_db + +# Run migrations +go run cmd/migrate/main.go +``` -### Architecture Overview +5. **Start the Application** -This project follows Clean Architecture principles with three main layers: +```bash +# Development mode +go run cmd/server/main.go -1. **Domain Layer** (`internal/domain/`) +# Or with hot reload +air +``` - - Contains business models and interfaces - - Defines repository interfaces - - Contains domain-specific errors +The server will start at `http://localhost:8080` -2. **Service Layer** (`internal/service/`) +## ๐Ÿ” Authentication & Authorization - - Implements business logic - - Orchestrates data flow between repositories - - Handles error wrapping +### **Multi-Layer Security System** -3. **API Layer** (`internal/api/`) - - Handles HTTP requests/responses - - Manages request validation - - Uses middleware for cross-cutting concerns +The system implements a **comprehensive authorization framework** combining: -### Error Handling +#### **1. RBAC (Role-Based Access Control)** -The project implements a hierarchical error handling system: +- **Fast O(1) permission checks** +- **Predefined roles**: Admin, Instructor, Student +- **Static permission mappings** -1. **Domain Errors** (`internal/domain/errors.go`) +#### **2. ABAC (Attribute-Based Access Control)** - - Base error types for business rules - - Used across all layers +- **Context-aware policies** +- **Dynamic access control** +- **Enrollment-based restrictions** -2. **Service Errors** (`internal/service/errors.go`) +### **Usage Example** - - Wraps domain errors with context - - Adds operation and entity information +```go +// Check authorization +allowed, err := authService.IsAuthorized(ctx, &auth.AccessRequest{ + Subject: &auth.AuthContext{ + UserID: "user_123", + Role: types.UserRoleStudent, + }, + Resource: &auth.Resource{ + Type: "internship", + ID: "internship_456", + }, + Action: auth.PermissionViewInternship, +}) +``` -3. **API Error Handling** (`internal/api/middleware/error_handler.go`) - - Converts errors to HTTP responses - - Maintains consistent error format +## ๐Ÿ’ณ Payment System -## Adding New Features +### **Multi-Gateway Support** -### Step 1: Domain Layer +- **Razorpay** (Primary - India-focused) +- **Stripe** (International) +- **Extensible architecture** for additional gateways -1. Create a new directory in `internal/domain//` -2. Create `model.go`: +### **Payment Flow** -```go -package feature +```mermaid +sequenceDiagram + participant U as User + participant A as API + participant P as Payment Service + participant R as Razorpay + participant W as Webhook Handler -type YourModel struct { - ID string `json:"id" bson:"_id,omitempty"` - Name string `json:"name" bson:"name"` - CreatedAt time.Time `json:"created_at" bson:"created_at"` - UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` -} + U->>A: Create Payment Request + A->>P: Process Payment + P->>R: Create Payment Order + R->>U: Redirect to Payment Page + U->>R: Complete Payment + R->>W: Send Webhook + W->>A: Update Payment Status + A->>U: Payment Confirmation ``` -3. Create `repository.go`: +### **Webhook Processing** ```go -package feature - -type Repository interface { - Create(ctx context.Context, model *YourModel) error - FindByID(ctx context.Context, id string) (*YourModel, error) - // Add other methods as needed +// Webhook handler for payment events +func (h *WebhookHandler) ProcessPaymentWebhook(ctx context.Context, payload []byte) error { + // Verify webhook signature + // Process payment status + // Update enrollment status + // Send notifications + return nil } ``` -### Step 2: Repository Implementation +## ๐Ÿ“š Core Features -1. Create `internal/repository//repository.go`: +### **1. Internship Management** -```go -package feature +- **CRUD Operations**: Create, read, update, delete internships +- **Batch Management**: Organize internships into batches +- **Category System**: Categorize internships by domain +- **Content Management**: Upload and manage course materials -import ( - "context" - "github.com/your-username/ecommerce/internal/domain/feature" - "go.mongodb.org/mongo-driver/mongo" -) +### **2. Enrollment System** -type repository struct { - db *mongo.Database -} +- **Application Workflow**: Apply for internships +- **Payment Integration**: Seamless payment processing +- **Status Tracking**: Monitor application progress +- **Peer Applications**: View other applicants -func NewRepository(db *mongo.Database) feature.Repository { - return &repository{db: db} -} +### **3. User Management** -// Implement the interface methods -``` +- **Role-Based Access**: Admin, Instructor, Student roles +- **Profile Management**: User profiles and preferences +- **Onboarding**: User registration and setup +- **Authentication**: JWT-based authentication -### Step 3: Service Layer +### **4. Discount System** -1. Create `internal/service/.go`: +- **Coupon Management**: Create and manage discount codes +- **Validation Rules**: Usage limits, expiration dates +- **Multiple Types**: Percentage, fixed amount, BOGO +- **Auto-Application**: Best discount selection + +## ๐Ÿ”ง Development Guide + +### **Adding New Features** + +#### **Step 1: Domain Layer** ```go -package service +// internal/domain/feature/model.go +type Feature struct { + ID string `json:"id"` + Name string `json:"name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} -import ( - "context" - "github.com/your-username/ecommerce/internal/domain/feature" -) +// internal/domain/feature/repository.go +type Repository interface { + Create(ctx context.Context, feature *Feature) error + GetByID(ctx context.Context, id string) (*Feature, error) + List(ctx context.Context, filter interface{}) ([]*Feature, error) + Update(ctx context.Context, id string, feature *Feature) error + Delete(ctx context.Context, id string) error +} +``` -type FeatureService struct { - repo feature.Repository +#### **Step 2: Repository Implementation** + +```go +// internal/repository/ent/feature.go +type featureRepository struct { + client *ent.Client } -func NewFeatureService(repo feature.Repository) *FeatureService { - return &FeatureService{repo: repo} +func NewFeatureRepository(client *ent.Client) domain.Repository { + return &featureRepository{client: client} } -// Add service methods with business logic +// Implement interface methods ``` -### Step 4: API Handler - -1. Create `internal/api/v1/.go`: +#### **Step 3: Service Layer** ```go -package v1 +// internal/service/feature.go +type FeatureService interface { + CreateFeature(ctx context.Context, req *CreateFeatureRequest) (*Feature, error) + GetFeature(ctx context.Context, id string) (*Feature, error) + ListFeatures(ctx context.Context, filter interface{}) ([]*Feature, error) + UpdateFeature(ctx context.Context, id string, req *UpdateFeatureRequest) (*Feature, error) + DeleteFeature(ctx context.Context, id string) error +} -import ( - "github.com/gin-gonic/gin" - "net/http" -) +type featureService struct { + repo domain.Repository + logger *logger.Logger +} +``` +#### **Step 4: API Handler** + +```go +// internal/api/v1/feature.go type FeatureHandler struct { - service *service.FeatureService + service service.FeatureService + logger *logger.Logger } -func NewFeatureHandler(service *service.FeatureService) *FeatureHandler { - return &FeatureHandler{service: service} +func (h *FeatureHandler) CreateFeature(c *gin.Context) { + // Handle HTTP request + // Validate input + // Call service + // Return response } +``` + +#### **Step 5: Update Router** -// Add handler methods +```go +// internal/api/router.go +func (r *Router) setupFeatureRoutes() { + features := r.v1.Group("/features") + features.Use(middleware.AuthRequired()) + { + features.POST("/", r.handlers.CreateFeature) + features.GET("/:id", r.handlers.GetFeature) + features.PUT("/:id", r.handlers.UpdateFeature) + features.DELETE("/:id", r.handlers.DeleteFeature) + } +} ``` -### Step 5: Update Router +### **Testing Strategy** -Add your new routes in `internal/api/router/router.go`: +#### **Unit Tests** ```go -feature := v1.Group("/feature") -{ - feature.POST("/", handlers.CreateFeature) - feature.GET("/:id", handlers.GetFeature) - // Add other routes +func TestFeatureService_CreateFeature(t *testing.T) { + // Arrange + mockRepo := &MockFeatureRepository{} + service := NewFeatureService(mockRepo, logger) + + // Act + result, err := service.CreateFeature(ctx, request) + + // Assert + assert.NoError(t, err) + assert.NotNil(t, result) } ``` -## Best Practices +#### **Integration Tests** -### Code Organization +```go +func TestFeatureAPI_CreateFeature(t *testing.T) { + // Setup test server + // Make HTTP request + // Assert response +} +``` + +## ๐Ÿ“Š API Documentation -- Keep packages small and focused -- Use meaningful package names -- Follow Go naming conventions -- Use interfaces for abstraction +### **Swagger Documentation** -### Error Handling +Access the interactive API documentation at: -- Use domain errors for business rule violations -- Wrap errors with context at service layer -- Return appropriate HTTP status codes +- **Development**: `http://localhost:8080/swagger/index.html` +- **Production**: `https://api.codegeeky.com/swagger/index.html` -### Testing +### **Key Endpoints** -1. **Unit Tests** +#### **Authentication** + +``` +POST /v1/auth/login # User login +POST /v1/auth/register # User registration +POST /v1/auth/refresh # Refresh JWT token +``` - - Test domain logic - - Test service layer business rules - - Use mocks for dependencies +#### **Internships** -2. **Integration Tests** - - Test repository implementations - - Test API endpoints +``` +GET /v1/internships # List internships +POST /v1/internships # Create internship +GET /v1/internships/:id # Get internship details +PUT /v1/internships/:id # Update internship +DELETE /v1/internships/:id # Delete internship +``` -### Validation +#### **Enrollments** -- Implement request validation using validator package -- Add domain-level validation in models -- Add service-level business rule validation +``` +POST /v1/internships/:id/apply # Apply for internship +GET /v1/enrollments # List user enrollments +GET /v1/enrollments/:id # Get enrollment details +PUT /v1/enrollments/:id/status # Update enrollment status +``` -### Documentation +#### **Payments** -- Add godoc comments to exported types and functions -- Keep README updated with new features -- Document API endpoints +``` +POST /v1/payments/create # Create payment +POST /v1/payments/webhooks/:gateway # Payment webhooks +GET /v1/payments/:id/status # Check payment status +``` -## Contributing +## ๐Ÿš€ Deployment -1. Create a new branch for your feature +### **Docker Deployment** ```bash -git checkout -b feature/your-feature-name +# Build image +docker build -t codegeeky-backend . + +# Run container +docker run -p 8080:8080 \ + -e POSTGRES_HOST=your-db-host \ + -e POSTGRES_PASSWORD=your-password \ + codegeeky-backend ``` -2. Follow the feature implementation steps above - -3. Write tests for your changes +### **Docker Compose** + +```yaml +version: "3.8" +services: + app: + build: . + ports: + - "8080:8080" + environment: + - POSTGRES_HOST=postgres + depends_on: + - postgres + - redis + + postgres: + image: postgres:14 + environment: + POSTGRES_DB: codegeeky_db + POSTGRES_USER: codegeeky_user + POSTGRES_PASSWORD: your_password + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + +volumes: + postgres_data: +``` -4. Submit a pull request +### **Production Checklist** -## License +- [ ] **Environment Variables**: Configure all production settings +- [ ] **Database**: Set up PostgreSQL with proper indexes +- [ ] **Redis**: Configure Redis for caching and sessions +- [ ] **SSL/TLS**: Enable HTTPS with valid certificates +- [ ] **Monitoring**: Set up logging and monitoring +- [ ] **Backup**: Configure database backups +- [ ] **Security**: Review security settings +- [ ] **Performance**: Optimize for production load -[Your License Here] +## ๐Ÿ“ˆ Monitoring & Observability -# MongoDB Database Utilities +### **Health Checks** -This package provides a set of utilities and extensions for working with MongoDB in a more streamlined way, while still leveraging the official MongoDB Go driver. +``` +GET /health # Basic health check +GET /health/detailed # Detailed system status +``` -## Features +### **Metrics** -- **FilterBuilder**: Create MongoDB filters with chainable methods -- **PipelineBuilder**: Build MongoDB aggregation pipelines with a fluent API -- **QueryOptions**: Simplify pagination, sorting, and other query options -- **ExtendedRepository**: Enhanced repository with additional helper methods +- **Request Rate**: Requests per second +- **Response Time**: Average response time +- **Error Rate**: Error percentage +- **Payment Success Rate**: Payment processing success +- **Enrollment Conversion**: Application to enrollment rate -## Usage Examples +### **Logging** -### Filter Builder +Structured logging with correlation IDs: ```go -// Create a filter with multiple conditions -filter := mongoUtils.NewFilter(). - Eq("status", "active"). - Gt("age", 18). - In("roles", []string{"user", "admin"}). - Build() - -// Use the filter with your repository -users, err := userRepo.Find(ctx, filter) +logger.Infow("Payment processed successfully", + "payment_id", paymentID, + "user_id", userID, + "amount", amount, + "gateway", "razorpay", +) ``` -### Pipeline Builder +## ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป Let's Build Together! ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป -```go -// Create an aggregation pipeline -pipeline := mongoUtils.NewPipeline(). - Match(bson.M{"status": "active"}). - Lookup("departments", "department_id", "_id", "department"). - Unwind("$department", true). - Sort(bson.D{{"created_at", -1}}). - Skip(10). - Limit(20). - Build() - -// Convert to mongo.Pipeline and use with repository -mongoPipeline := mongoUtils.ToPipeline(pipeline) -var results []UserWithDepartment -err := repo.AggregateWithPipeline(ctx, mongoPipeline, &results) -``` +Whether you're a newbie coder or a wizard ๐Ÿง™โ€โ™€๏ธ, your perspective is golden. Take a peek at our: -### Query Options +๐Ÿ“œ [Contribution Guidelines](CONTRIBUTING.md) -```go -// Create query options for pagination and sorting -opts := mongoUtils.QueryOptions{ - Page: 2, - Limit: 25, - Sort: bson.D{{"created_at", -1}}, -} +๐Ÿ—๏ธ [Local Development Setup](SETUP.md) -// Use with repository -var users []User -err := repo.FindWithQueryOptions(ctx, filter, opts, &users) -``` +โค๏ธ [Code of Conduct](CODE_OF_CONDUCT.md) -### Extended Repository +## Contributors -```go -// Create an extended repository -repo := mongoRepository.NewExtendedRepository(db.Collection("users")) + + + + +## Repo Activity + +![Alt](https://repobeats.axiom.co/api/embed/588693ad1c029a586edf259cd23abdbae35280f7.svg "Repobeats analytics image") + +## ๐Ÿค Contributing + +### **Development Workflow** + +1. **Fork the repository** +2. **Create feature branch**: `git checkout -b feature/amazing-feature` +3. **Make changes** following the development guide +4. **Write tests** for new functionality +5. **Update documentation** as needed +6. **Submit pull request** with clear description + +### **Code Standards** -// Use extended methods -totalCount, err := repo.PaginatedFind(ctx, filter, 1, 20, &users) +- **Go Format**: Use `gofmt` for code formatting +- **Linting**: Run `golangci-lint` for code quality +- **Testing**: Maintain >80% test coverage +- **Documentation**: Add godoc comments for exported functions -// Create indexes easily -_, err := repo.CreateIndex(ctx, - bson.D{{"email", 1}}, - options.Index().SetUnique(true)) +### **Commit Convention** + +``` +feat: add new payment gateway support +fix: resolve webhook signature validation issue +docs: update API documentation +test: add integration tests for enrollment flow +refactor: improve error handling in auth service ``` -## Hybrid Approach +## ๐Ÿ“„ License + +CodeGeeky is a commercial open source company, which means some parts of this open source repository require a commercial license. The concept is called "Open Core" where the core technology (99%) is fully open source, licensed under [AGPLv3](https://opensource.org/license/agpl-v3) and the last 1% is covered under a commercial license (["/ee" Enterprise Edition"]). + +> [!TIP] +> We work closely with the community and always invite feedback about what should be open and what is fine to be commercial. This list is not set and stone and we have moved things from commercial to open in the past. Please open a [discussion](https://github.com/omkar273/codegeeky/discussions) if you feel like something is wrong. + +## ๐Ÿ†˜ Support + +- **Documentation**: [docs/](docs/) directory +- **Issues**: [GitHub Issues](https://github.com/omkar273/codegeeky/issues) +- **Discussions**: [GitHub Discussions](https://github.com/omkar273/codegeeky/discussions) + +--- -This package supports a hybrid approach to database operations: +## ๐ŸŽฏ Key Benefits -1. Use the utility helpers for common patterns and simplified queries -2. Fall back to raw MongoDB queries for complex operations -3. Extend the repositories as needed for domain-specific operations +โœ… **Scalable Architecture** - Clean architecture with dependency injection +โœ… **Advanced Security** - RBAC + ABAC authorization system +โœ… **Payment Integration** - Multi-gateway payment processing +โœ… **Event-Driven** - Real-time webhook and notification system +โœ… **Comprehensive Testing** - Unit, integration, and e2e tests +โœ… **Production Ready** - Monitoring, logging, and deployment guides -This gives you the best of both worlds - convenience for common tasks and full flexibility when needed. -# ksp-go-backend -# codegeeky-lms-backend +This LMS platform provides a **robust foundation** for building **modern learning platforms** with **enterprise-grade features** and **scalable architecture**. diff --git a/Untitled-1.yaml b/Untitled-1.yaml deleted file mode 100644 index 6317711..0000000 --- a/Untitled-1.yaml +++ /dev/null @@ -1,125 +0,0 @@ - -DGP - - state - - district - - tehsil - - village - - police station - - sub-division - - circle - - -- superior - - police - same post - - police - same post - -user : - - name - - phone - - email - - password - - rank - -- post - - name - - power - - description - - assignable_by : post[] - -constable: - - name - - state - - district - - tehsil - - village - - police_station - - sub_division - - circle - -police naik - - name - - underlying - 5-10 constables - -head constable - - name - - underlying - 5-10 naik - -sub inspector - - name - - underlying - 5-10 head constable - -inspector - - name - - underlying - 5-10 sub inspector - - -- post operations(promote, demote, transfer, remove) - - state - - district - - tehsil - - village - - police_station - - sub_division - - circle - -single responsiblity -- entity -- station -- post -- dept - -(current) - - -before_post -before_station - -after_post -after_station - -operated_by - - - -user1: - superior: - - user2 - superior: - - user3 - superior: - - user4 - subordinate: - - user4 - - user5 - -user4 : - user1: - null -user5 : - user1: - null -user6 : - user1: - null -user7 : - user1: - null -user8 : - user1: - null - -n : n -> no of posts above that user - - -scalablity - - -permission - - entity_type - - entity_id - - action - - is_allowed - - allow_entity : user/dept/station - - allow_entity_id - - permissions: read/write/execute/delete - diff --git a/cmd/server/main.go b/cmd/server/main.go index 965f8e9..dfc6a14 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -12,6 +12,7 @@ import ( "github.com/omkar273/codegeeky/internal/config" "github.com/omkar273/codegeeky/internal/httpclient" "github.com/omkar273/codegeeky/internal/logger" + gateway "github.com/omkar273/codegeeky/internal/payment" "github.com/omkar273/codegeeky/internal/postgres" pubsubRouter "github.com/omkar273/codegeeky/internal/pubsub/router" "github.com/omkar273/codegeeky/internal/repository" @@ -31,7 +32,7 @@ import ( // @contact.email support@example.com // @host localhost:8080 -// @BasePath /api/v1 +// @BasePath /v1 // @securityDefinitions.apikey Authorization // @in header @@ -70,6 +71,9 @@ func main() { // http client httpclient.NewDefaultClient, + // payment gateway registry + gateway.InitializeProviders, + // user repository repository.NewUserRepository, @@ -82,6 +86,15 @@ func main() { // discount repository repository.NewDiscountRepository, + // payment repository + repository.NewPaymentRepository, + + // internship enrollment repository + repository.NewInternshipEnrollmentRepository, + + // internship batch repository + repository.NewInternshipBatchRepository, + // pubsub router pubsubRouter.NewRouter, ), @@ -99,8 +112,12 @@ func main() { service.NewUserService, service.NewOnboardingService, service.NewInternshipService, + service.NewInternshipBatchService, service.NewCategoryService, service.NewDiscountService, + service.NewPricingService, + service.NewPaymentService, + service.NewInternshipEnrollmentService, )) // factory layer diff --git a/docs/AUTHORIZATION_GUIDE.md b/docs/AUTHORIZATION_GUIDE.md deleted file mode 100644 index b3e7a34..0000000 --- a/docs/AUTHORIZATION_GUIDE.md +++ /dev/null @@ -1,999 +0,0 @@ -# ๐Ÿ” Authorization Guide: Best Practices & Implementation - -This guide provides comprehensive documentation on implementing secure access control in your Go backend system using Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC). - -## ๐Ÿ“‹ Table of Contents - -1. [System Overview](#system-overview) -2. [Authorization Architecture](#authorization-architecture) -3. [Best Practices](#best-practices) -4. [Implementation Patterns](#implementation-patterns) -5. [Adding New Rules & Policies](#adding-new-rules--policies) -6. [Testing Authorization](#testing-authorization) -7. [Performance Optimization](#performance-optimization) -8. [Security Guidex2lines](#security-guidelines) -9. [Troubleshooting](#troubleshooting) - -## ๐Ÿ—๏ธ System Overview - -Your authorization system implements a **multi-layered security approach** combining: - -- **RBAC (Role-Based Access Control)**: Fast, simple role-to-permission mapping -- **ABAC (Attribute-Based Access Control)**: Fine-grained, context-aware authorization -- **Caching Layer**: Performance optimization for authorization decisions -- **Audit Trail**: Complete logging of all authorization decisions - -### Key Components - -```mermaid -graph TB - A[HTTP Request] --> B[Authentication Middleware] - B --> C[Authorization Middleware] - C --> D[Authorization Coordinator] - D --> E[RBAC Service] - D --> F[ABAC Service] - E --> G[Permission Check] - F --> H[Policy Evaluation] - G --> I[Final Decision] - H --> I - I --> J[Business Logic] -``` - -## ๐Ÿ›๏ธ Authorization Architecture - -### Layer 1: Route-Level Protection (Middleware) - -**Purpose**: Coarse-grained access control at the HTTP route level - -```go -// Basic role-based protection -router.POST("/internships", - middleware.AuthenticateMiddleware(), - middleware.AuthorizationMiddleware(), - middleware.RequireInstructorOrAdmin(), - handler.CreateInternship, -) - -// Permission-based protection -router.GET("/internships/:id", - middleware.AuthenticateMiddleware(), - middleware.AuthorizationMiddleware(), - middleware.RequirePermission(auth.PermissionViewInternship, "internship"), - handler.GetInternship, -) -``` - -### Layer 2: Service-Level Authorization (Business Logic) - -**Purpose**: Context-aware, fine-grained authorization with business rules - -```go -func (s *internshipService) Update(ctx context.Context, id string, req *dto.UpdateInternshipRequest) error { - // Get auth context from middleware - authCtx := auth.GetAuthContextFromContext(ctx) - - // Get existing resource for context - existing, err := s.internshipRepo.Get(ctx, id) - if err != nil { - return err - } - - // Create detailed authorization request - authRequest := &auth.AccessRequest{ - Subject: authCtx, - Resource: &auth.Resource{ - Type: "internship", - ID: id, - Attributes: map[string]interface{}{ - "created_by": existing.CreatedBy, // Ownership check - "status": existing.Status, // Status-based rules - "department": existing.Department, // Department restrictions - }, - }, - Action: auth.PermissionUpdateInternship, - } - - // Check authorization - allowed, err := s.authzService.IsAuthorized(ctx, authRequest) - if err != nil { - return fmt.Errorf("authorization check failed: %w", err) - } - if !allowed { - return ierr.ErrPermissionDenied - } - - // Proceed with business logic - return s.internshipRepo.Update(ctx, id, req) -} -``` - -### Layer 3: Data-Level Security (Repository) - -**Purpose**: Row-level security and data filtering - -```go -func (r *internshipRepository) List(ctx context.Context, filter *types.InternshipFilter) ([]*domain.Internship, error) { - authCtx := auth.GetAuthContextFromContext(ctx) - - // Apply role-based filtering at database level - switch authCtx.Role { - case types.UserRoleStudent: - filter.Status = []string{"published"} - filter.EnrolledUserID = authCtx.UserID - case types.UserRoleInstructor: - filter.CreatedByOrPublished = authCtx.UserID - case types.UserRoleAdmin: - // No additional filtering for admins - } - - return r.db.Find(filter) -} -``` - -## โœ… Best Practices - -### 1. Defense in Depth - -**โŒ Wrong: Single Point of Authorization** - -```go -// Only checking at middleware level -router.POST("/internships", middleware.RequireInstructor(), handler.CreateInternship) - -func (h *handler) CreateInternship(c *gin.Context) { - // No additional authorization checks - s.internshipService.Create(ctx, req) // Dangerous! -} -``` - -**โœ… Correct: Multi-Layer Authorization** - -```go -// Layer 1: Route protection -router.POST("/internships", - middleware.AuthenticateMiddleware(), - middleware.RequireInstructorOrAdmin(), - handler.CreateInternship, -) - -// Layer 2: Service-level authorization -func (s *internshipService) Create(ctx context.Context, req *dto.CreateInternshipRequest) error { - authCtx := auth.GetAuthContextFromContext(ctx) - - // Always verify authorization in service layer - allowed, err := s.authzService.IsAuthorized(ctx, &auth.AccessRequest{ - Subject: authCtx, - Resource: &auth.Resource{Type: "internship"}, - Action: auth.PermissionCreateInternship, - }) - - if !allowed { - return ierr.ErrPermissionDenied - } - - // Business logic... -} -``` - -### 2. Principle of Least Privilege - -**Always start with the minimum required permissions:** - -```go -// Role hierarchy (from least to most privileged) -types.UserRoleStudent // Read-only access to enrolled content -types.UserRoleInstructor // Manage own content + read others -types.UserRoleAdmin // Full system access -``` - -### 3. Explicit Deny by Default - -```go -func (s *rbacService) HasPermission(role types.UserRole, permission auth.Permission) bool { - permissions, exists := s.rolePermissions[role] - if !exists { - return false // Explicit deny if role doesn't exist - } - - for _, p := range permissions { - if p == permission { - return true // Explicit allow - } - } - - return false // Explicit deny if permission not found -} -``` - -### 4. Context-Aware Authorization - -**Include relevant context in authorization decisions:** - -```go -// Good: Include business context -authRequest := &auth.AccessRequest{ - Subject: authCtx, - Resource: &auth.Resource{ - Type: "content", - ID: contentID, - Attributes: map[string]interface{}{ - "internship_id": internshipID, - "required_progress": 0.75, // 75% completion required - "access_window": accessWindow, // Time-based access - "content_level": "advanced", // Difficulty level - }, - }, - Action: auth.PermissionViewLectures, -} -``` - -## ๐Ÿ”ง Implementation Patterns - -### Pattern 1: Simple Role-Based Access - -**Use Case**: Basic operations that only depend on user role - -```go -// Route level -router.GET("/admin/users", middleware.RequireAdmin(), handler.ListUsers) - -// Service level -func (s *userService) GetAll(ctx context.Context) ([]*domain.User, error) { - authCtx := auth.GetAuthContextFromContext(ctx) - - // Simple role check - if authCtx.Role != types.UserRoleAdmin { - return nil, ierr.ErrPermissionDenied - } - - return s.userRepo.GetAll(ctx) -} -``` - -### Pattern 2: Ownership-Based Access - -**Use Case**: Users can only modify resources they own - -```go -func (s *internshipService) Update(ctx context.Context, id string, req *dto.UpdateRequest) error { - authCtx := auth.GetAuthContextFromContext(ctx) - existing, _ := s.internshipRepo.Get(ctx, id) - - // Check ownership or admin role - if existing.CreatedBy != authCtx.UserID && authCtx.Role != types.UserRoleAdmin { - return ierr.ErrPermissionDenied - } - - return s.internshipRepo.Update(ctx, id, req) -} -``` - -### Pattern 3: Enrollment-Based Access - -**Use Case**: Students can only access content they're enrolled in - -```go -func (s *contentService) GetContent(ctx context.Context, contentID, internshipID string) (*domain.Content, error) { - authCtx := auth.GetAuthContextFromContext(ctx) - - // For students, check enrollment - if authCtx.Role == types.UserRoleStudent { - enrolled, err := s.enrollmentService.IsUserEnrolled(ctx, authCtx.UserID, internshipID) - if err != nil || !enrolled { - return nil, ierr.ErrPermissionDenied - } - } - - return s.contentRepo.Get(ctx, contentID) -} -``` - -### Pattern 4: Progress-Based Access - -**Use Case**: Content unlocked based on completion progress - -```go -func (s *contentService) AccessAdvancedContent(ctx context.Context, contentID string) error { - authCtx := auth.GetAuthContextFromContext(ctx) - - // Check user progress - progress, err := s.progressService.GetUserProgress(ctx, authCtx.UserID) - if err != nil { - return err - } - - if progress.CompletionPercentage < 0.75 { // 75% required - return ierr.NewErrorf("insufficient progress: %d%% completed, 75%% required", - int(progress.CompletionPercentage*100)) - } - - return nil -} -``` - -## ๐Ÿ†• Adding New Rules & Policies - -### Adding a New Permission - -**Step 1: Define the Permission** - -```go -// internal/domain/auth/model.go -const ( - // Existing permissions... - PermissionCreateInternship Permission = "internship:create" - PermissionViewInternship Permission = "internship:view" - - // Add new permission - PermissionExportData Permission = "data:export" -) -``` - -**Step 2: Update Role-Permission Mapping** - -```go -// internal/auth/rbac/service.go -func initializeRolePermissions() map[types.UserRole][]auth.Permission { - return map[types.UserRole][]auth.Permission{ - types.UserRoleAdmin: { - // Existing permissions... - auth.PermissionCreateInternship, - auth.PermissionViewInternship, - - // Add new permission to appropriate roles - auth.PermissionExportData, - }, - types.UserRoleInstructor: { - auth.PermissionCreateInternship, - auth.PermissionViewInternship, - // Instructors can export their own data - auth.PermissionExportData, - }, - types.UserRoleStudent: { - auth.PermissionViewInternship, - // Students cannot export data - }, - } -} -``` - -**Step 3: Use in Middleware** - -```go -// Add route protection -router.GET("/export/data", - middleware.RequirePermission(auth.PermissionExportData, "data"), - handler.ExportData, -) -``` - -**Step 4: Use in Service Layer** - -```go -func (s *dataService) Export(ctx context.Context, filters *ExportFilters) (*ExportResult, error) { - authCtx := auth.GetAuthContextFromContext(ctx) - - // Check permission - allowed, err := s.authzService.IsAuthorized(ctx, &auth.AccessRequest{ - Subject: authCtx, - Resource: &auth.Resource{Type: "data"}, - Action: auth.PermissionExportData, - }) - - if !allowed { - return nil, ierr.ErrPermissionDenied - } - - // Apply role-based filtering - if authCtx.Role == types.UserRoleInstructor { - filters.CreatedBy = authCtx.UserID // Only own data - } - - return s.dataRepo.Export(ctx, filters) -} -``` - -### Adding a New ABAC Policy - -**Step 1: Create the Policy** - -```go -// internal/auth/abac/policies/department_policy.go -package policies - -type DepartmentAccessPolicy struct{} - -func (p *DepartmentAccessPolicy) GetName() string { - return "DepartmentAccess" -} - -func (p *DepartmentAccessPolicy) GetPriority() int { - return 150 // Medium priority -} - -func (p *DepartmentAccessPolicy) Applies(request *auth.AccessRequest) bool { - // Apply to content access in specific departments - return request.Resource.Type == "content" && - request.Resource.Attributes["department"] != nil -} - -func (p *DepartmentAccessPolicy) Evaluate(ctx context.Context, request *auth.AccessRequest) (Decision, error) { - // Get user's department - userDept, exists := request.Subject.Attributes["department"] - if !exists { - return Decision{ - Allow: false, - Reason: "User department not specified", - }, nil - } - - // Get resource department - resourceDept := request.Resource.Attributes["department"] - - // Allow access if departments match OR user is admin - if userDept == resourceDept || request.Subject.Role == types.UserRoleAdmin { - return Decision{ - Allow: true, - Reason: "Department access granted", - }, nil - } - - return Decision{ - Allow: false, - Reason: "Cross-department access denied", - }, nil -} -``` - -**Step 2: Register the Policy** - -```go -// internal/auth/abac/service.go -func (s *service) registerDefaultPolicies() { - // Existing policies... - s.RegisterPolicy(NewEnrollmentBasedAccessPolicy()) - s.RegisterPolicy(NewOwnershipPolicy()) - - // Add new policy - s.RegisterPolicy(NewDepartmentAccessPolicy()) -} -``` - -**Step 3: Use in Service Layer** - -```go -func (s *contentService) GetByDepartment(ctx context.Context, contentID string) (*domain.Content, error) { - authCtx := auth.GetAuthContextFromContext(ctx) - - // Get content to check department - content, err := s.contentRepo.Get(ctx, contentID) - if err != nil { - return nil, err - } - - // Create authorization request with department context - authRequest := &auth.AccessRequest{ - Subject: authCtx, - Resource: &auth.Resource{ - Type: "content", - ID: contentID, - Attributes: map[string]interface{}{ - "department": content.Department, - }, - }, - Action: auth.PermissionViewContent, - } - - allowed, err := s.authzService.IsAuthorized(ctx, authRequest) - if !allowed { - return nil, ierr.ErrPermissionDenied - } - - return content, nil -} -``` - -### Adding a New Role - -**Step 1: Define the Role** - -```go -// internal/types/user.go -type UserRole string - -const ( - UserRoleStudent UserRole = "STUDENT" - UserRoleInstructor UserRole = "INSTRUCTOR" - UserRoleAdmin UserRole = "ADMIN" - - // Add new role - UserRoleModerator UserRole = "MODERATOR" -) -``` - -**Step 2: Update Role Permissions** - -```go -// internal/auth/rbac/service.go -func initializeRolePermissions() map[types.UserRole][]auth.Permission { - return map[types.UserRole][]auth.Permission{ - // Existing roles... - - types.UserRoleModerator: { - // Moderator permissions (between instructor and admin) - auth.PermissionViewInternship, - auth.PermissionViewLectures, - auth.PermissionViewAssignments, - auth.PermissionViewResources, - auth.PermissionViewAnalytics, - auth.PermissionManageUsers, // Can manage users but not system config - }, - } -} -``` - -**Step 3: Update Middleware** - -```go -// Add convenience middleware for new role -func RequireModeratorOrAdmin() gin.HandlerFunc { - return RequireRole(types.UserRoleModerator, types.UserRoleAdmin) -} -``` - -**Step 4: Update Role Hierarchy** - -```go -// internal/auth/rbac/service.go -func GetRoleHierarchy() map[types.UserRole]int { - return map[types.UserRole]int{ - types.UserRoleAdmin: 4, // Highest level - types.UserRoleModerator: 3, // New level - types.UserRoleInstructor: 2, // Middle level - types.UserRoleStudent: 1, // Basic level - } -} -``` - -## ๐Ÿงช Testing Authorization - -### Unit Testing RBAC - -```go -func TestRBACPermissions(t *testing.T) { - authzService := auth.NewAuthorizationService(logger) - - tests := []struct { - role types.UserRole - permission auth.Permission - expected bool - }{ - {types.UserRoleAdmin, auth.PermissionCreateInternship, true}, - {types.UserRoleInstructor, auth.PermissionCreateInternship, true}, - {types.UserRoleStudent, auth.PermissionCreateInternship, false}, - {types.UserRoleStudent, auth.PermissionViewInternship, true}, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("%s_%s", test.role, test.permission), func(t *testing.T) { - result := authzService.CheckRolePermission(test.role, test.permission) - assert.Equal(t, test.expected, result) - }) - } -} -``` - -### Integration Testing ABAC - -```go -func TestEnrollmentBasedAccess(t *testing.T) { - ctx := context.Background() - authzService := setupAuthzService(t) - - t.Run("enrolled student can access content", func(t *testing.T) { - authRequest := &auth.AccessRequest{ - Subject: &auth.AuthContext{ - UserID: "student-123", - Role: types.UserRoleStudent, - Attributes: map[string]interface{}{ - "enrolled_internships": []string{"internship-456"}, - }, - }, - Resource: &auth.Resource{ - Type: "content", - Attributes: map[string]interface{}{ - "internship_id": "internship-456", - }, - }, - Action: auth.PermissionViewLectures, - } - - allowed, err := authzService.IsAuthorized(ctx, authRequest) - assert.NoError(t, err) - assert.True(t, allowed) - }) - - t.Run("non-enrolled student cannot access content", func(t *testing.T) { - authRequest := &auth.AccessRequest{ - Subject: &auth.AuthContext{ - UserID: "student-123", - Role: types.UserRoleStudent, - Attributes: map[string]interface{}{ - "enrolled_internships": []string{"internship-789"}, - }, - }, - Resource: &auth.Resource{ - Type: "content", - Attributes: map[string]interface{}{ - "internship_id": "internship-456", // Different internship - }, - }, - Action: auth.PermissionViewLectures, - } - - allowed, err := authzService.IsAuthorized(ctx, authRequest) - assert.NoError(t, err) - assert.False(t, allowed) - }) -} -``` - -### End-to-End Testing - -```go -func TestInternshipAccessEndToEnd(t *testing.T) { - // Setup test server - server := setupTestServer(t) - defer server.Close() - - t.Run("instructor can create internship", func(t *testing.T) { - token := generateInstructorToken(t) - - resp, err := http.Post( - server.URL+"/api/v1/internships", - "application/json", - strings.NewReader(`{"title":"Test Internship","description":"Test"}`), - ) - resp.Header.Set("Authorization", "Bearer "+token) - - assert.NoError(t, err) - assert.Equal(t, http.StatusCreated, resp.StatusCode) - }) - - t.Run("student cannot create internship", func(t *testing.T) { - token := generateStudentToken(t) - - resp, err := http.Post( - server.URL+"/api/v1/internships", - "application/json", - strings.NewReader(`{"title":"Test Internship","description":"Test"}`), - ) - resp.Header.Set("Authorization", "Bearer "+token) - - assert.NoError(t, err) - assert.Equal(t, http.StatusForbidden, resp.StatusCode) - }) -} -``` - -## โšก Performance Optimization - -### 1. Cache Authorization Decisions - -```go -type AuthorizationCache struct { - cache map[string]*CachedDecision - ttl time.Duration - mu sync.RWMutex -} - -type CachedDecision struct { - Allowed bool - ExpiresAt time.Time -} - -func (c *AuthorizationCache) Get(key string) (bool, bool) { - c.mu.RLock() - defer c.mu.RUnlock() - - decision, exists := c.cache[key] - if !exists || time.Now().After(decision.ExpiresAt) { - return false, false - } - - return decision.Allowed, true -} - -func (c *AuthorizationCache) Set(key string, allowed bool) { - c.mu.Lock() - defer c.mu.Unlock() - - c.cache[key] = &CachedDecision{ - Allowed: allowed, - ExpiresAt: time.Now().Add(c.ttl), - } -} -``` - -### 2. Batch Authorization Checks - -```go -func (s *authzService) BatchAuthorize(ctx context.Context, requests []*auth.AccessRequest) ([]bool, error) { - results := make([]bool, len(requests)) - - // Group requests by user to optimize attribute loading - userGroups := groupRequestsByUser(requests) - - for userID, userRequests := range userGroups { - // Load user attributes once per user - userAttrs, _ := s.attributeLoader.LoadUserAttributes(ctx, userID) - - for _, req := range userRequests { - req.Subject.Attributes = userAttrs - allowed, _ := s.IsAuthorized(ctx, req) - results[req.Index] = allowed - } - } - - return results, nil -} -``` - -### 3. Database-Level Filtering - -```go -// Apply filters at SQL level to reduce data transfer -func (r *internshipRepository) ListAuthorized(ctx context.Context, authCtx *auth.AuthContext, filter *Filter) ([]*Internship, error) { - query := r.db.Select("*").From("internships") - - // Apply role-based filters at database level - switch authCtx.Role { - case types.UserRoleStudent: - query = query. - Join("enrollments e ON internships.id = e.internship_id"). - Where("e.user_id = ? AND internships.status = 'published'", authCtx.UserID) - case types.UserRoleInstructor: - query = query. - Where("internships.created_by = ? OR internships.status = 'published'", authCtx.UserID) - } - - var internships []*Internship - err := query.Load(&internships) - return internships, err -} -``` - -## ๐Ÿ›ก๏ธ Security Guidelines - -### 1. Always Validate Input - -```go -func (s *internshipService) Create(ctx context.Context, req *dto.CreateRequest) error { - // Validate input BEFORE authorization - if err := req.Validate(); err != nil { - return ierr.NewErrorf("invalid input").Mark(err) - } - - // Then check authorization - authCtx := auth.GetAuthContextFromContext(ctx) - allowed, err := s.authzService.IsAuthorized(ctx, &auth.AccessRequest{ - Subject: authCtx, - Resource: &auth.Resource{Type: "internship"}, - Action: auth.PermissionCreateInternship, - }) - - if !allowed { - return ierr.ErrPermissionDenied - } - - // Proceed with business logic -} -``` - -### 2. Log All Authorization Decisions - -```go -func (s *authzService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { - startTime := time.Now() - allowed, err := s.evaluate(ctx, request) - duration := time.Since(startTime) - - // Always log authorization decisions - s.auditLogger.LogAuthorizationDecision(ctx, &AuditEvent{ - UserID: request.Subject.UserID, - Action: string(request.Action), - Resource: request.Resource.Type, - ResourceID: request.Resource.ID, - Allowed: allowed, - Duration: duration, - Timestamp: time.Now(), - IPAddress: extractIPFromContext(ctx), - UserAgent: extractUserAgentFromContext(ctx), - Error: err, - }) - - return allowed, err -} -``` - -### 3. Rate Limit Authorization Checks - -```go -type RateLimitedAuthzService struct { - authzService auth.AuthorizationService - rateLimiter *rate.Limiter -} - -func (r *RateLimitedAuthzService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { - // Rate limit per user - userKey := request.Subject.UserID - if !r.rateLimiter.Allow(userKey) { - return false, ierr.NewErrorf("rate limit exceeded for user %s", userKey) - } - - return r.authzService.IsAuthorized(ctx, request) -} -``` - -### 4. Sanitize Sensitive Data - -```go -func (s *internshipService) GetByID(ctx context.Context, id string) (*dto.InternshipResponse, error) { - authCtx := auth.GetAuthContextFromContext(ctx) - - internship, err := s.internshipRepo.Get(ctx, id) - if err != nil { - return nil, err - } - - // Check authorization - allowed, err := s.checkAccess(ctx, authCtx, internship) - if !allowed { - return nil, ierr.ErrPermissionDenied - } - - // Sanitize response based on user role - response := &dto.InternshipResponse{Internship: *internship} - - // Remove sensitive fields for students - if authCtx.Role == types.UserRoleStudent { - response.Internship.InternalNotes = "" - response.Internship.InstructorEmail = "" - } - - return response, nil -} -``` - -## ๐Ÿ” Troubleshooting - -### Common Issues and Solutions - -#### Issue 1: Permission Denied Errors - -**Problem**: Users getting unexpected permission denied errors - -**Debugging Steps**: - -1. Check audit logs for the specific authorization decision -2. Verify user role and permissions -3. Check ABAC policy evaluation results - -```go -// Add detailed logging for debugging -func (s *authzService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { - s.logger.Debugw("Authorization check started", - "user_id", request.Subject.UserID, - "role", request.Subject.Role, - "action", request.Action, - "resource_type", request.Resource.Type, - "resource_id", request.Resource.ID, - ) - - // RBAC check - rbacAllowed := s.rbacService.HasPermission(request.Subject.Role, request.Action) - s.logger.Debugw("RBAC result", "allowed", rbacAllowed) - - if !rbacAllowed { - return false, nil - } - - // ABAC check - abacAllowed, err := s.abacService.Evaluate(ctx, request) - s.logger.Debugw("ABAC result", "allowed", abacAllowed, "error", err) - - return abacAllowed, err -} -``` - -#### Issue 2: Performance Problems - -**Problem**: Slow authorization checks affecting API response times - -**Solutions**: - -1. Implement caching for authorization decisions -2. Use database-level filtering -3. Optimize ABAC policy evaluation - -```go -// Performance monitoring -func (s *authzService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { - start := time.Now() - defer func() { - duration := time.Since(start) - if duration > 100*time.Millisecond { - s.logger.Warnw("Slow authorization check", - "duration_ms", duration.Milliseconds(), - "user_id", request.Subject.UserID, - "action", request.Action, - ) - } - }() - - return s.evaluate(ctx, request) -} -``` - -#### Issue 3: Cache Invalidation - -**Problem**: Stale authorization cache causing incorrect decisions - -**Solution**: Implement proper cache invalidation strategies - -```go -func (s *userService) UpdateUserRole(ctx context.Context, userID string, newRole types.UserRole) error { - // Update user role - err := s.userRepo.UpdateRole(ctx, userID, newRole) - if err != nil { - return err - } - - // Invalidate authorization cache for this user - s.authzCache.InvalidateUser(userID) - - // Log role change for audit - s.auditLogger.LogSecurityEvent(ctx, &SecurityEvent{ - Type: "role_change", - UserID: userID, - Metadata: map[string]interface{}{"new_role": newRole}, - Timestamp: time.Now(), - }) - - return nil -} -``` - -### Debug Commands - -```bash -# Check user permissions -curl -H "Authorization: Bearer $TOKEN" \ - http://localhost:8080/api/v1/auth/permissions - -# View audit logs -tail -f logs/authorization.log | jq '.level=="INFO" and .msg=="authorization_decision"' - -# Check authorization cache stats -curl -H "Authorization: Bearer $ADMIN_TOKEN" \ - http://localhost:8080/api/v1/admin/auth/cache/stats -``` - -## ๐Ÿ“š Additional Resources - -- [OWASP Authorization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) -- [NIST RBAC Standard](https://csrc.nist.gov/publications/detail/sp/800-162/final) -- [ABAC Guide](https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-162.pdf) - -## ๐Ÿ“ž Support - -For authorization-related issues: - -1. Check this guide and troubleshooting section -2. Review audit logs for detailed error information -3. Contact the security team for policy-related questions -4. File an issue in the project repository for bugs - ---- - -**Remember**: Security is everyone's responsibility. Always follow the principle of least privilege and validate all authorization decisions at multiple layers. diff --git a/docs/prds/AUTHORIZATION_GUIDE.md b/docs/prds/AUTHORIZATION_GUIDE.md index 8291376..b3e7a34 100644 --- a/docs/prds/AUTHORIZATION_GUIDE.md +++ b/docs/prds/AUTHORIZATION_GUIDE.md @@ -1,380 +1,631 @@ -# Authorization Guide: RBAC and ABAC Implementation - -This guide explains how to implement and use Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC) in your Go backend system. +# ๐Ÿ” Authorization Guide: Best Practices & Implementation + +This guide provides comprehensive documentation on implementing secure access control in your Go backend system using Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC). + +## ๐Ÿ“‹ Table of Contents + +1. [System Overview](#system-overview) +2. [Authorization Architecture](#authorization-architecture) +3. [Best Practices](#best-practices) +4. [Implementation Patterns](#implementation-patterns) +5. [Adding New Rules & Policies](#adding-new-rules--policies) +6. [Testing Authorization](#testing-authorization) +7. [Performance Optimization](#performance-optimization) +8. [Security Guidex2lines](#security-guidelines) +9. [Troubleshooting](#troubleshooting) + +## ๐Ÿ—๏ธ System Overview + +Your authorization system implements a **multi-layered security approach** combining: + +- **RBAC (Role-Based Access Control)**: Fast, simple role-to-permission mapping +- **ABAC (Attribute-Based Access Control)**: Fine-grained, context-aware authorization +- **Caching Layer**: Performance optimization for authorization decisions +- **Audit Trail**: Complete logging of all authorization decisions + +### Key Components + +```mermaid +graph TB + A[HTTP Request] --> B[Authentication Middleware] + B --> C[Authorization Middleware] + C --> D[Authorization Coordinator] + D --> E[RBAC Service] + D --> F[ABAC Service] + E --> G[Permission Check] + F --> H[Policy Evaluation] + G --> I[Final Decision] + H --> I + I --> J[Business Logic] +``` -## Overview +## ๐Ÿ›๏ธ Authorization Architecture -Our authorization system combines two approaches: +### Layer 1: Route-Level Protection (Middleware) -1. **RBAC (Role-Based Access Control)**: Controls access based on user roles (Student, Instructor, Admin) -2. **ABAC (Attribute-Based Access Control)**: Fine-grained access control based on attributes like ownership, enrollment, progress, etc. +**Purpose**: Coarse-grained access control at the HTTP route level -## Architecture +```go +// Basic role-based protection +router.POST("/internships", + middleware.AuthenticateMiddleware(), + middleware.AuthorizationMiddleware(), + middleware.RequireInstructorOrAdmin(), + handler.CreateInternship, +) -``` -Request โ†’ Authentication โ†’ Authorization (RBAC + ABAC) โ†’ Business Logic +// Permission-based protection +router.GET("/internships/:id", + middleware.AuthenticateMiddleware(), + middleware.AuthorizationMiddleware(), + middleware.RequirePermission(auth.PermissionViewInternship, "internship"), + handler.GetInternship, +) ``` -### Components +### Layer 2: Service-Level Authorization (Business Logic) -1. **Authorization Service** (`internal/auth/authorization.go`) -2. **Authorization Middleware** (`internal/rest/middleware/authorization.go`) -3. **Domain Models** (`internal/domain/auth/model.go`) -4. **ABAC Policies** (Embedded in authorization service) +**Purpose**: Context-aware, fine-grained authorization with business rules -## User Roles and Permissions +```go +func (s *internshipService) Update(ctx context.Context, id string, req *dto.UpdateInternshipRequest) error { + // Get auth context from middleware + authCtx := auth.GetAuthContextFromContext(ctx) -### Role Hierarchy + // Get existing resource for context + existing, err := s.internshipRepo.Get(ctx, id) + if err != nil { + return err + } -``` -Admin (Full Access) -โ”œโ”€โ”€ All Instructor permissions -โ”œโ”€โ”€ System configuration -โ”œโ”€โ”€ User management -โ””โ”€โ”€ Analytics + // Create detailed authorization request + authRequest := &auth.AccessRequest{ + Subject: authCtx, + Resource: &auth.Resource{ + Type: "internship", + ID: id, + Attributes: map[string]interface{}{ + "created_by": existing.CreatedBy, // Ownership check + "status": existing.Status, // Status-based rules + "department": existing.Department, // Department restrictions + }, + }, + Action: auth.PermissionUpdateInternship, + } -Instructor (Content Creator) -โ”œโ”€โ”€ Create/Update/Delete own internships -โ”œโ”€โ”€ View all published internships -โ”œโ”€โ”€ Access all content -โ””โ”€โ”€ View analytics for own content + // Check authorization + allowed, err := s.authzService.IsAuthorized(ctx, authRequest) + if err != nil { + return fmt.Errorf("authorization check failed: %w", err) + } + if !allowed { + return ierr.ErrPermissionDenied + } -Student (Consumer) -โ”œโ”€โ”€ View enrolled internships -โ”œโ”€โ”€ Access content based on enrollment and progress -โ””โ”€โ”€ Limited content access + // Proceed with business logic + return s.internshipRepo.Update(ctx, id, req) +} ``` -### Permission Matrix +### Layer 3: Data-Level Security (Repository) -| Permission | Admin | Instructor | Student | -| -------------------------- | ----- | ---------- | -------- | -| `internship:create` | โœ… | โœ… | โŒ | -| `internship:update` | โœ… | โœ…\* | โŒ | -| `internship:delete` | โœ… | โœ…\* | โŒ | -| `internship:view` | โœ… | โœ… | โœ…\*\* | -| `content:lectures:view` | โœ… | โœ… | โœ…\*\*\* | -| `content:assignments:view` | โœ… | โœ… | โœ…\*\*\* | -| `content:resources:view` | โœ… | โœ… | โœ…\*\*\* | -| `users:manage` | โœ… | โŒ | โŒ | -| `system:config` | โœ… | โŒ | โŒ | +**Purpose**: Row-level security and data filtering -_\* Only own content_ -_\*\* Only enrolled internships_ -_\*\*\* Only enrolled internships with sufficient progress_ +```go +func (r *internshipRepository) List(ctx context.Context, filter *types.InternshipFilter) ([]*domain.Internship, error) { + authCtx := auth.GetAuthContextFromContext(ctx) + + // Apply role-based filtering at database level + switch authCtx.Role { + case types.UserRoleStudent: + filter.Status = []string{"published"} + filter.EnrolledUserID = authCtx.UserID + case types.UserRoleInstructor: + filter.CreatedByOrPublished = authCtx.UserID + case types.UserRoleAdmin: + // No additional filtering for admins + } -## RBAC Implementation + return r.db.Find(filter) +} +``` + +## โœ… Best Practices + +### 1. Defense in Depth -### 1. Role Definition +**โŒ Wrong: Single Point of Authorization** ```go -// internal/types/user.go -type UserRole string +// Only checking at middleware level +router.POST("/internships", middleware.RequireInstructor(), handler.CreateInternship) -const ( - UserRoleStudent UserRole = "STUDENT" - UserRoleInstructor UserRole = "INSTRUCTOR" - UserRoleAdmin UserRole = "ADMIN" -) +func (h *handler) CreateInternship(c *gin.Context) { + // No additional authorization checks + s.internshipService.Create(ctx, req) // Dangerous! +} ``` -### 2. Permission Definition +**โœ… Correct: Multi-Layer Authorization** ```go -// internal/domain/auth/model.go -type Permission string - -const ( - PermissionCreateInternship Permission = "internship:create" - PermissionViewInternship Permission = "internship:view" - // ... more permissions +// Layer 1: Route protection +router.POST("/internships", + middleware.AuthenticateMiddleware(), + middleware.RequireInstructorOrAdmin(), + handler.CreateInternship, ) + +// Layer 2: Service-level authorization +func (s *internshipService) Create(ctx context.Context, req *dto.CreateInternshipRequest) error { + authCtx := auth.GetAuthContextFromContext(ctx) + + // Always verify authorization in service layer + allowed, err := s.authzService.IsAuthorized(ctx, &auth.AccessRequest{ + Subject: authCtx, + Resource: &auth.Resource{Type: "internship"}, + Action: auth.PermissionCreateInternship, + }) + + if !allowed { + return ierr.ErrPermissionDenied + } + + // Business logic... +} ``` -### 3. Role-Permission Mapping +### 2. Principle of Least Privilege + +**Always start with the minimum required permissions:** ```go -// internal/auth/authorization.go -func initializeRolePermissions() map[types.UserRole][]auth.Permission { - return map[types.UserRole][]auth.Permission{ - types.UserRoleAdmin: { - // All permissions - }, - types.UserRoleInstructor: { - auth.PermissionCreateInternship, - auth.PermissionUpdateInternship, - // ... instructor permissions - }, - types.UserRoleStudent: { - auth.PermissionViewInternship, - }, +// Role hierarchy (from least to most privileged) +types.UserRoleStudent // Read-only access to enrolled content +types.UserRoleInstructor // Manage own content + read others +types.UserRoleAdmin // Full system access +``` + +### 3. Explicit Deny by Default + +```go +func (s *rbacService) HasPermission(role types.UserRole, permission auth.Permission) bool { + permissions, exists := s.rolePermissions[role] + if !exists { + return false // Explicit deny if role doesn't exist } + + for _, p := range permissions { + if p == permission { + return true // Explicit allow + } + } + + return false // Explicit deny if permission not found } ``` -## ABAC Implementation +### 4. Context-Aware Authorization -### 1. Policy Interface +**Include relevant context in authorization decisions:** ```go -type ABACPolicy interface { - Evaluate(ctx context.Context, request *auth.AccessRequest) (bool, error) - GetName() string +// Good: Include business context +authRequest := &auth.AccessRequest{ + Subject: authCtx, + Resource: &auth.Resource{ + Type: "content", + ID: contentID, + Attributes: map[string]interface{}{ + "internship_id": internshipID, + "required_progress": 0.75, // 75% completion required + "access_window": accessWindow, // Time-based access + "content_level": "advanced", // Difficulty level + }, + }, + Action: auth.PermissionViewLectures, } ``` -### 2. Built-in Policies +## ๐Ÿ”ง Implementation Patterns -#### Enrollment Policy +### Pattern 1: Simple Role-Based Access -Students can only access content of internships they're enrolled in. +**Use Case**: Basic operations that only depend on user role ```go -type EnrollmentBasedAccessPolicy struct{} +// Route level +router.GET("/admin/users", middleware.RequireAdmin(), handler.ListUsers) + +// Service level +func (s *userService) GetAll(ctx context.Context) ([]*domain.User, error) { + authCtx := auth.GetAuthContextFromContext(ctx) -func (p *EnrollmentBasedAccessPolicy) Evaluate(ctx context.Context, request *auth.AccessRequest) (bool, error) { - // Check if student is enrolled in the internship - if request.Subject.Role != types.UserRoleStudent { - return true, nil // Policy doesn't apply to non-students + // Simple role check + if authCtx.Role != types.UserRoleAdmin { + return nil, ierr.ErrPermissionDenied } - // Get enrolled internships from user attributes - enrolledInternships := request.Subject.Attributes["enrolled_internships"] - // ... validation logic + return s.userRepo.GetAll(ctx) } ``` -#### Ownership Policy +### Pattern 2: Ownership-Based Access -Users can only modify resources they created. +**Use Case**: Users can only modify resources they own ```go -type OwnershipPolicy struct{} +func (s *internshipService) Update(ctx context.Context, id string, req *dto.UpdateRequest) error { + authCtx := auth.GetAuthContextFromContext(ctx) + existing, _ := s.internshipRepo.Get(ctx, id) + + // Check ownership or admin role + if existing.CreatedBy != authCtx.UserID && authCtx.Role != types.UserRoleAdmin { + return ierr.ErrPermissionDenied + } -func (p *OwnershipPolicy) Evaluate(ctx context.Context, request *auth.AccessRequest) (bool, error) { - // Check if user owns the resource - resourceOwner := request.Resource.Attributes["created_by"] - return request.Subject.UserID == resourceOwner, nil + return s.internshipRepo.Update(ctx, id, req) } ``` -#### Progress Policy +### Pattern 3: Enrollment-Based Access -Students need sufficient progress to access advanced content. +**Use Case**: Students can only access content they're enrolled in ```go -type ContentAccessPolicy struct{} +func (s *contentService) GetContent(ctx context.Context, contentID, internshipID string) (*domain.Content, error) { + authCtx := auth.GetAuthContextFromContext(ctx) + + // For students, check enrollment + if authCtx.Role == types.UserRoleStudent { + enrolled, err := s.enrollmentService.IsUserEnrolled(ctx, authCtx.UserID, internshipID) + if err != nil || !enrolled { + return nil, ierr.ErrPermissionDenied + } + } -func (p *ContentAccessPolicy) Evaluate(ctx context.Context, request *auth.AccessRequest) (bool, error) { - // Check user progress against required progress - userProgress := request.Subject.Attributes["progress"] - requiredProgress := request.Resource.Attributes["required_progress"] - // ... comparison logic + return s.contentRepo.Get(ctx, contentID) } ``` -#### Time-based Policy +### Pattern 4: Progress-Based Access -Content access based on time windows. +**Use Case**: Content unlocked based on completion progress ```go -type TimeBasedAccessPolicy struct{} +func (s *contentService) AccessAdvancedContent(ctx context.Context, contentID string) error { + authCtx := auth.GetAuthContextFromContext(ctx) + + // Check user progress + progress, err := s.progressService.GetUserProgress(ctx, authCtx.UserID) + if err != nil { + return err + } + + if progress.CompletionPercentage < 0.75 { // 75% required + return ierr.NewErrorf("insufficient progress: %d%% completed, 75%% required", + int(progress.CompletionPercentage*100)) + } -func (p *TimeBasedAccessPolicy) Evaluate(ctx context.Context, request *auth.AccessRequest) (bool, error) { - // Check access time windows - startTime := request.Resource.Attributes["access_start_time"] - endTime := request.Resource.Attributes["access_end_time"] - // ... time validation logic + return nil } ``` -## Usage Examples +## ๐Ÿ†• Adding New Rules & Policies -### 1. Service Layer Authorization +### Adding a New Permission + +**Step 1: Define the Permission** + +```go +// internal/domain/auth/model.go +const ( + // Existing permissions... + PermissionCreateInternship Permission = "internship:create" + PermissionViewInternship Permission = "internship:view" + + // Add new permission + PermissionExportData Permission = "data:export" +) +``` + +**Step 2: Update Role-Permission Mapping** ```go -func (s *internshipService) Create(ctx context.Context, req *dto.CreateInternshipRequest, userID string, userRole types.UserRole) (*domainInternship.Internship, error) { - // Create authorization request - authRequest := &domainAuth.AccessRequest{ - Subject: &domainAuth.AuthContext{ - UserID: userID, - Role: userRole, +// internal/auth/rbac/service.go +func initializeRolePermissions() map[types.UserRole][]auth.Permission { + return map[types.UserRole][]auth.Permission{ + types.UserRoleAdmin: { + // Existing permissions... + auth.PermissionCreateInternship, + auth.PermissionViewInternship, + + // Add new permission to appropriate roles + auth.PermissionExportData, }, - Resource: &domainAuth.Resource{ - Type: "internship", + types.UserRoleInstructor: { + auth.PermissionCreateInternship, + auth.PermissionViewInternship, + // Instructors can export their own data + auth.PermissionExportData, + }, + types.UserRoleStudent: { + auth.PermissionViewInternship, + // Students cannot export data }, - Action: domainAuth.PermissionCreateInternship, } +} +``` - // Check authorization - allowed, err := s.authzService.IsAuthorized(ctx, authRequest) - if err != nil { - return nil, err - } +**Step 3: Use in Middleware** + +```go +// Add route protection +router.GET("/export/data", + middleware.RequirePermission(auth.PermissionExportData, "data"), + handler.ExportData, +) +``` + +**Step 4: Use in Service Layer** + +```go +func (s *dataService) Export(ctx context.Context, filters *ExportFilters) (*ExportResult, error) { + authCtx := auth.GetAuthContextFromContext(ctx) + + // Check permission + allowed, err := s.authzService.IsAuthorized(ctx, &auth.AccessRequest{ + Subject: authCtx, + Resource: &auth.Resource{Type: "data"}, + Action: auth.PermissionExportData, + }) if !allowed { return nil, ierr.ErrPermissionDenied } - // Business logic continues... + // Apply role-based filtering + if authCtx.Role == types.UserRoleInstructor { + filters.CreatedBy = authCtx.UserID // Only own data + } + + return s.dataRepo.Export(ctx, filters) } ``` -### 2. Middleware-based Authorization +### Adding a New ABAC Policy + +**Step 1: Create the Policy** ```go -// Role-based middleware -internshipGroup.POST("", - middleware.RequireInstructorOrAdmin(), - handler.CreateInternship, -) +// internal/auth/abac/policies/department_policy.go +package policies -// Permission-based middleware -internshipGroup.POST("", - middleware.RequirePermission(domainAuth.PermissionCreateInternship, "internship"), - handler.CreateInternship, -) +type DepartmentAccessPolicy struct{} + +func (p *DepartmentAccessPolicy) GetName() string { + return "DepartmentAccess" +} + +func (p *DepartmentAccessPolicy) GetPriority() int { + return 150 // Medium priority +} + +func (p *DepartmentAccessPolicy) Applies(request *auth.AccessRequest) bool { + // Apply to content access in specific departments + return request.Resource.Type == "content" && + request.Resource.Attributes["department"] != nil +} + +func (p *DepartmentAccessPolicy) Evaluate(ctx context.Context, request *auth.AccessRequest) (Decision, error) { + // Get user's department + userDept, exists := request.Subject.Attributes["department"] + if !exists { + return Decision{ + Allow: false, + Reason: "User department not specified", + }, nil + } + + // Get resource department + resourceDept := request.Resource.Attributes["department"] + + // Allow access if departments match OR user is admin + if userDept == resourceDept || request.Subject.Role == types.UserRoleAdmin { + return Decision{ + Allow: true, + Reason: "Department access granted", + }, nil + } + + return Decision{ + Allow: false, + Reason: "Cross-department access denied", + }, nil +} ``` -### 3. Content Access with ABAC +**Step 2: Register the Policy** ```go -func (h *InternshipHandler) AccessInternshipContent(c *gin.Context) { - authContext := middleware.GetAuthContext(c) +// internal/auth/abac/service.go +func (s *service) registerDefaultPolicies() { + // Existing policies... + s.RegisterPolicy(NewEnrollmentBasedAccessPolicy()) + s.RegisterPolicy(NewOwnershipPolicy()) + + // Add new policy + s.RegisterPolicy(NewDepartmentAccessPolicy()) +} +``` - // Create authorization request with attributes - authRequest := &domainAuth.AccessRequest{ - Subject: authContext, - Resource: &domainAuth.Resource{ +**Step 3: Use in Service Layer** + +```go +func (s *contentService) GetByDepartment(ctx context.Context, contentID string) (*domain.Content, error) { + authCtx := auth.GetAuthContextFromContext(ctx) + + // Get content to check department + content, err := s.contentRepo.Get(ctx, contentID) + if err != nil { + return nil, err + } + + // Create authorization request with department context + authRequest := &auth.AccessRequest{ + Subject: authCtx, + Resource: &auth.Resource{ Type: "content", ID: contentID, Attributes: map[string]interface{}{ - "internship_id": internshipID, - "content_type": contentType, - "required_progress": 0.5, // 50% progress required - "access_start_time": time.Now().Add(-24*time.Hour), - "access_end_time": time.Now().Add(24*time.Hour), + "department": content.Department, }, }, - Action: domainAuth.PermissionViewLectures, + Action: auth.PermissionViewContent, + } + + allowed, err := s.authzService.IsAuthorized(ctx, authRequest) + if !allowed { + return nil, ierr.ErrPermissionDenied } - allowed, err := h.authzService.IsAuthorized(c.Request.Context(), authRequest) - // ... handle result + return content, nil } ``` -## Setting Up User Attributes +### Adding a New Role -### 1. In Authentication Middleware +**Step 1: Define the Role** ```go -func AuthorizationMiddleware(...) gin.HandlerFunc { - return func(c *gin.Context) { - // Get user from database - user, err := userRepo.Get(c.Request.Context(), userID) +// internal/types/user.go +type UserRole string - // Create auth context with attributes - authContext := &domainAuth.AuthContext{ - UserID: userID, - Email: userEmail, - Role: user.Role, - Attributes: map[string]interface{}{ - "enrolled_internships": getUserEnrollments(userID), - "progress": getUserProgress(userID), - "subscription_level": user.SubscriptionLevel, - }, - } +const ( + UserRoleStudent UserRole = "STUDENT" + UserRoleInstructor UserRole = "INSTRUCTOR" + UserRoleAdmin UserRole = "ADMIN" - c.Set("auth_context", authContext) - c.Next() - } -} + // Add new role + UserRoleModerator UserRole = "MODERATOR" +) ``` -### 2. Dynamic Attribute Loading +**Step 2: Update Role Permissions** ```go -func (s *authorizationService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { - // Load additional attributes if needed - if request.Action == auth.PermissionViewLectures { - enrollments, err := s.enrollmentService.GetUserEnrollments(ctx, request.Subject.UserID) - if err == nil { - request.Subject.Attributes["enrolled_internships"] = enrollments - } - } +// internal/auth/rbac/service.go +func initializeRolePermissions() map[types.UserRole][]auth.Permission { + return map[types.UserRole][]auth.Permission{ + // Existing roles... - // Continue with authorization... + types.UserRoleModerator: { + // Moderator permissions (between instructor and admin) + auth.PermissionViewInternship, + auth.PermissionViewLectures, + auth.PermissionViewAssignments, + auth.PermissionViewResources, + auth.PermissionViewAnalytics, + auth.PermissionManageUsers, // Can manage users but not system config + }, + } } ``` -## Best Practices +**Step 3: Update Middleware** -### 1. Security +```go +// Add convenience middleware for new role +func RequireModeratorOrAdmin() gin.HandlerFunc { + return RequireRole(types.UserRoleModerator, types.UserRoleAdmin) +} +``` -- Always validate user input before authorization checks -- Use least privilege principle -- Log authorization decisions for audit trails -- Implement rate limiting for authorization checks +**Step 4: Update Role Hierarchy** + +```go +// internal/auth/rbac/service.go +func GetRoleHierarchy() map[types.UserRole]int { + return map[types.UserRole]int{ + types.UserRoleAdmin: 4, // Highest level + types.UserRoleModerator: 3, // New level + types.UserRoleInstructor: 2, // Middle level + types.UserRoleStudent: 1, // Basic level + } +} +``` -### 2. Performance +## ๐Ÿงช Testing Authorization -- Cache role-permission mappings -- Minimize database calls in ABAC policies -- Use efficient data structures for attribute lookups -- Consider async attribute loading for non-critical attributes +### Unit Testing RBAC -### 3. Maintainability +```go +func TestRBACPermissions(t *testing.T) { + authzService := auth.NewAuthorizationService(logger) + + tests := []struct { + role types.UserRole + permission auth.Permission + expected bool + }{ + {types.UserRoleAdmin, auth.PermissionCreateInternship, true}, + {types.UserRoleInstructor, auth.PermissionCreateInternship, true}, + {types.UserRoleStudent, auth.PermissionCreateInternship, false}, + {types.UserRoleStudent, auth.PermissionViewInternship, true}, + } -- Keep policies simple and focused -- Document policy logic clearly -- Use descriptive permission names -- Separate authorization logic from business logic + for _, test := range tests { + t.Run(fmt.Sprintf("%s_%s", test.role, test.permission), func(t *testing.T) { + result := authzService.CheckRolePermission(test.role, test.permission) + assert.Equal(t, test.expected, result) + }) + } +} +``` -### 4. Testing +### Integration Testing ABAC ```go -func TestInternshipCreation(t *testing.T) { - // Test RBAC - t.Run("instructor can create internship", func(t *testing.T) { - authRequest := &auth.AccessRequest{ - Subject: &auth.AuthContext{Role: types.UserRoleInstructor}, - Resource: &auth.Resource{Type: "internship"}, - Action: auth.PermissionCreateInternship, - } - - allowed, err := authzService.IsAuthorized(ctx, authRequest) - assert.NoError(t, err) - assert.True(t, allowed) - }) +func TestEnrollmentBasedAccess(t *testing.T) { + ctx := context.Background() + authzService := setupAuthzService(t) - t.Run("student cannot create internship", func(t *testing.T) { + t.Run("enrolled student can access content", func(t *testing.T) { authRequest := &auth.AccessRequest{ - Subject: &auth.AuthContext{Role: types.UserRoleStudent}, - Resource: &auth.Resource{Type: "internship"}, - Action: auth.PermissionCreateInternship, + Subject: &auth.AuthContext{ + UserID: "student-123", + Role: types.UserRoleStudent, + Attributes: map[string]interface{}{ + "enrolled_internships": []string{"internship-456"}, + }, + }, + Resource: &auth.Resource{ + Type: "content", + Attributes: map[string]interface{}{ + "internship_id": "internship-456", + }, + }, + Action: auth.PermissionViewLectures, } allowed, err := authzService.IsAuthorized(ctx, authRequest) assert.NoError(t, err) - assert.False(t, allowed) + assert.True(t, allowed) }) -} -func TestContentAccess(t *testing.T) { - // Test ABAC - t.Run("enrolled student can access content", func(t *testing.T) { + t.Run("non-enrolled student cannot access content", func(t *testing.T) { authRequest := &auth.AccessRequest{ Subject: &auth.AuthContext{ - Role: types.UserRoleStudent, + UserID: "student-123", + Role: types.UserRoleStudent, Attributes: map[string]interface{}{ - "enrolled_internships": []string{"internship-123"}, + "enrolled_internships": []string{"internship-789"}, }, }, Resource: &auth.Resource{ Type: "content", Attributes: map[string]interface{}{ - "internship_id": "internship-123", + "internship_id": "internship-456", // Different internship }, }, Action: auth.PermissionViewLectures, @@ -382,80 +633,367 @@ func TestContentAccess(t *testing.T) { allowed, err := authzService.IsAuthorized(ctx, authRequest) assert.NoError(t, err) - assert.True(t, allowed) + assert.False(t, allowed) }) } ``` -## Common Use Cases +### End-to-End Testing + +```go +func TestInternshipAccessEndToEnd(t *testing.T) { + // Setup test server + server := setupTestServer(t) + defer server.Close() + + t.Run("instructor can create internship", func(t *testing.T) { + token := generateInstructorToken(t) + + resp, err := http.Post( + server.URL+"/api/v1/internships", + "application/json", + strings.NewReader(`{"title":"Test Internship","description":"Test"}`), + ) + resp.Header.Set("Authorization", "Bearer "+token) + + assert.NoError(t, err) + assert.Equal(t, http.StatusCreated, resp.StatusCode) + }) + + t.Run("student cannot create internship", func(t *testing.T) { + token := generateStudentToken(t) + + resp, err := http.Post( + server.URL+"/api/v1/internships", + "application/json", + strings.NewReader(`{"title":"Test Internship","description":"Test"}`), + ) + resp.Header.Set("Authorization", "Bearer "+token) + + assert.NoError(t, err) + assert.Equal(t, http.StatusForbidden, resp.StatusCode) + }) +} +``` + +## โšก Performance Optimization + +### 1. Cache Authorization Decisions + +```go +type AuthorizationCache struct { + cache map[string]*CachedDecision + ttl time.Duration + mu sync.RWMutex +} + +type CachedDecision struct { + Allowed bool + ExpiresAt time.Time +} + +func (c *AuthorizationCache) Get(key string) (bool, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + + decision, exists := c.cache[key] + if !exists || time.Now().After(decision.ExpiresAt) { + return false, false + } + + return decision.Allowed, true +} + +func (c *AuthorizationCache) Set(key string, allowed bool) { + c.mu.Lock() + defer c.mu.Unlock() + + c.cache[key] = &CachedDecision{ + Allowed: allowed, + ExpiresAt: time.Now().Add(c.ttl), + } +} +``` + +### 2. Batch Authorization Checks -### 1. Creating an Internship +```go +func (s *authzService) BatchAuthorize(ctx context.Context, requests []*auth.AccessRequest) ([]bool, error) { + results := make([]bool, len(requests)) -- **RBAC**: Only instructors and admins can create -- **ABAC**: No additional restrictions + // Group requests by user to optimize attribute loading + userGroups := groupRequestsByUser(requests) -### 2. Viewing an Internship + for userID, userRequests := range userGroups { + // Load user attributes once per user + userAttrs, _ := s.attributeLoader.LoadUserAttributes(ctx, userID) -- **RBAC**: All authenticated users can view -- **ABAC**: Students can only view enrolled internships + for _, req := range userRequests { + req.Subject.Attributes = userAttrs + allowed, _ := s.IsAuthorized(ctx, req) + results[req.Index] = allowed + } + } -### 3. Updating an Internship + return results, nil +} +``` -- **RBAC**: Instructors and admins can update -- **ABAC**: Instructors can only update their own internships +### 3. Database-Level Filtering -### 4. Accessing Content +```go +// Apply filters at SQL level to reduce data transfer +func (r *internshipRepository) ListAuthorized(ctx context.Context, authCtx *auth.AuthContext, filter *Filter) ([]*Internship, error) { + query := r.db.Select("*").From("internships") + + // Apply role-based filters at database level + switch authCtx.Role { + case types.UserRoleStudent: + query = query. + Join("enrollments e ON internships.id = e.internship_id"). + Where("e.user_id = ? AND internships.status = 'published'", authCtx.UserID) + case types.UserRoleInstructor: + query = query. + Where("internships.created_by = ? OR internships.status = 'published'", authCtx.UserID) + } -- **RBAC**: Students can access content -- **ABAC**: Must be enrolled + sufficient progress + within time window + var internships []*Internship + err := query.Load(&internships) + return internships, err +} +``` -### 5. Admin Operations +## ๐Ÿ›ก๏ธ Security Guidelines -- **RBAC**: Only admins -- **ABAC**: No additional restrictions +### 1. Always Validate Input -## Extending the System +```go +func (s *internshipService) Create(ctx context.Context, req *dto.CreateRequest) error { + // Validate input BEFORE authorization + if err := req.Validate(); err != nil { + return ierr.NewErrorf("invalid input").Mark(err) + } -### Adding New Roles + // Then check authorization + authCtx := auth.GetAuthContextFromContext(ctx) + allowed, err := s.authzService.IsAuthorized(ctx, &auth.AccessRequest{ + Subject: authCtx, + Resource: &auth.Resource{Type: "internship"}, + Action: auth.PermissionCreateInternship, + }) -1. Add role constant to `internal/types/user.go` -2. Update role-permission mapping in `initializeRolePermissions()` -3. Update middleware functions if needed + if !allowed { + return ierr.ErrPermissionDenied + } -### Adding New Permissions + // Proceed with business logic +} +``` -1. Add permission constant to `internal/domain/auth/model.go` -2. Update role-permission mappings -3. Use in service layer or middleware +### 2. Log All Authorization Decisions -### Adding New ABAC Policies +```go +func (s *authzService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { + startTime := time.Now() + allowed, err := s.evaluate(ctx, request) + duration := time.Since(startTime) + + // Always log authorization decisions + s.auditLogger.LogAuthorizationDecision(ctx, &AuditEvent{ + UserID: request.Subject.UserID, + Action: string(request.Action), + Resource: request.Resource.Type, + ResourceID: request.Resource.ID, + Allowed: allowed, + Duration: duration, + Timestamp: time.Now(), + IPAddress: extractIPFromContext(ctx), + UserAgent: extractUserAgentFromContext(ctx), + Error: err, + }) -1. Implement `ABACPolicy` interface -2. Add to `initializeABACPolicies()` -3. Test thoroughly + return allowed, err +} +``` -### Custom Attribute Providers +### 3. Rate Limit Authorization Checks ```go -type AttributeProvider interface { - LoadAttributes(ctx context.Context, userID string) (map[string]interface{}, error) +type RateLimitedAuthzService struct { + authzService auth.AuthorizationService + rateLimiter *rate.Limiter } -type EnrollmentAttributeProvider struct { - enrollmentRepo EnrollmentRepository +func (r *RateLimitedAuthzService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { + // Rate limit per user + userKey := request.Subject.UserID + if !r.rateLimiter.Allow(userKey) { + return false, ierr.NewErrorf("rate limit exceeded for user %s", userKey) + } + + return r.authzService.IsAuthorized(ctx, request) } +``` + +### 4. Sanitize Sensitive Data + +```go +func (s *internshipService) GetByID(ctx context.Context, id string) (*dto.InternshipResponse, error) { + authCtx := auth.GetAuthContextFromContext(ctx) -func (p *EnrollmentAttributeProvider) LoadAttributes(ctx context.Context, userID string) (map[string]interface{}, error) { - enrollments, err := p.enrollmentRepo.GetByUserID(ctx, userID) + internship, err := s.internshipRepo.Get(ctx, id) if err != nil { return nil, err } - return map[string]interface{}{ - "enrolled_internships": extractInternshipIDs(enrollments), - "enrollment_dates": extractEnrollmentDates(enrollments), - }, nil + // Check authorization + allowed, err := s.checkAccess(ctx, authCtx, internship) + if !allowed { + return nil, ierr.ErrPermissionDenied + } + + // Sanitize response based on user role + response := &dto.InternshipResponse{Internship: *internship} + + // Remove sensitive fields for students + if authCtx.Role == types.UserRoleStudent { + response.Internship.InternalNotes = "" + response.Internship.InstructorEmail = "" + } + + return response, nil } ``` -This authorization system provides a flexible and scalable approach to access control, combining the simplicity of RBAC with the fine-grained control of ABAC. +## ๐Ÿ” Troubleshooting + +### Common Issues and Solutions + +#### Issue 1: Permission Denied Errors + +**Problem**: Users getting unexpected permission denied errors + +**Debugging Steps**: + +1. Check audit logs for the specific authorization decision +2. Verify user role and permissions +3. Check ABAC policy evaluation results + +```go +// Add detailed logging for debugging +func (s *authzService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { + s.logger.Debugw("Authorization check started", + "user_id", request.Subject.UserID, + "role", request.Subject.Role, + "action", request.Action, + "resource_type", request.Resource.Type, + "resource_id", request.Resource.ID, + ) + + // RBAC check + rbacAllowed := s.rbacService.HasPermission(request.Subject.Role, request.Action) + s.logger.Debugw("RBAC result", "allowed", rbacAllowed) + + if !rbacAllowed { + return false, nil + } + + // ABAC check + abacAllowed, err := s.abacService.Evaluate(ctx, request) + s.logger.Debugw("ABAC result", "allowed", abacAllowed, "error", err) + + return abacAllowed, err +} +``` + +#### Issue 2: Performance Problems + +**Problem**: Slow authorization checks affecting API response times + +**Solutions**: + +1. Implement caching for authorization decisions +2. Use database-level filtering +3. Optimize ABAC policy evaluation + +```go +// Performance monitoring +func (s *authzService) IsAuthorized(ctx context.Context, request *auth.AccessRequest) (bool, error) { + start := time.Now() + defer func() { + duration := time.Since(start) + if duration > 100*time.Millisecond { + s.logger.Warnw("Slow authorization check", + "duration_ms", duration.Milliseconds(), + "user_id", request.Subject.UserID, + "action", request.Action, + ) + } + }() + + return s.evaluate(ctx, request) +} +``` + +#### Issue 3: Cache Invalidation + +**Problem**: Stale authorization cache causing incorrect decisions + +**Solution**: Implement proper cache invalidation strategies + +```go +func (s *userService) UpdateUserRole(ctx context.Context, userID string, newRole types.UserRole) error { + // Update user role + err := s.userRepo.UpdateRole(ctx, userID, newRole) + if err != nil { + return err + } + + // Invalidate authorization cache for this user + s.authzCache.InvalidateUser(userID) + + // Log role change for audit + s.auditLogger.LogSecurityEvent(ctx, &SecurityEvent{ + Type: "role_change", + UserID: userID, + Metadata: map[string]interface{}{"new_role": newRole}, + Timestamp: time.Now(), + }) + + return nil +} +``` + +### Debug Commands + +```bash +# Check user permissions +curl -H "Authorization: Bearer $TOKEN" \ + http://localhost:8080/api/v1/auth/permissions + +# View audit logs +tail -f logs/authorization.log | jq '.level=="INFO" and .msg=="authorization_decision"' + +# Check authorization cache stats +curl -H "Authorization: Bearer $ADMIN_TOKEN" \ + http://localhost:8080/api/v1/admin/auth/cache/stats +``` + +## ๐Ÿ“š Additional Resources + +- [OWASP Authorization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) +- [NIST RBAC Standard](https://csrc.nist.gov/publications/detail/sp/800-162/final) +- [ABAC Guide](https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-162.pdf) + +## ๐Ÿ“ž Support + +For authorization-related issues: + +1. Check this guide and troubleshooting section +2. Review audit logs for detailed error information +3. Contact the security team for policy-related questions +4. File an issue in the project repository for bugs + +--- + +**Remember**: Security is everyone's responsibility. Always follow the principle of least privilege and validate all authorization decisions at multiple layers. diff --git a/docs/prds/ENROLLMENT_API_EXAMPLES.md b/docs/prds/ENROLLMENT_API_EXAMPLES.md new file mode 100644 index 0000000..6a7e496 --- /dev/null +++ b/docs/prds/ENROLLMENT_API_EXAMPLES.md @@ -0,0 +1,692 @@ +# ๐Ÿš€ LMS Enrollment API Examples & Test Scenarios + +## ๐Ÿ“‹ Overview + +This document provides comprehensive API examples, request/response samples, and test scenarios for the LMS enrollment workflow with Razorpay integration. + +## ๐Ÿ”„ Complete Enrollment Flow Examples + +### Scenario 1: Paid Course Enrollment + +#### Step 1: Get Course Information + +```http +GET /api/v1/internships/int_01HKZM7X9P0QJ2K3L4M5N6O7P8 +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... +``` + +**Response:** + +```json +{ + "internship": { + "id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P8", + "title": "Full Stack Web Development Bootcamp", + "description": "Comprehensive 12-week program covering React, Node.js, and MongoDB", + "price": "4999.00", + "currency": "INR", + "flat_discount": "2000.00", + "percentage_discount": "0.00", + "final_price": "2999.00", + "duration_in_weeks": 12, + "level": "intermediate", + "prerequisites": ["basic_programming", "html_css"], + "skills": ["react", "nodejs", "mongodb", "javascript"], + "categories": [ + { + "id": "cat_web_development", + "name": "Web Development" + } + ], + "enrollment_info": { + "can_enroll": true, + "is_enrolled": false, + "enrollment_deadline": "2024-03-30T23:59:59Z", + "available_spots": 25, + "enrolled_count": 45 + } + } +} +``` + +#### Step 2: Initialize Enrollment + +```http +POST /api/v1/enrollments/initialize +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... +Content-Type: application/json + +{ + "course_id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P8", + "coupon_code": "EARLY50", + "payment_method_preference": "razorpay", + "success_url": "https://myapp.com/enrollment/success", + "cancel_url": "https://myapp.com/enrollment/cancel", + "metadata": { + "source": "web_app", + "campaign": "spring_2024", + "referrer": "google_ads" + } +} +``` + +**Response:** + +```json +{ + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9I", + "status": "pending", + "payment_required": true, + "course": { + "id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P8", + "title": "Full Stack Web Development Bootcamp" + }, + "pricing": { + "original_amount": "4999.00", + "discount_amount": "2500.00", + "coupon_discount": "500.00", + "final_amount": "2499.00", + "currency": "INR", + "tax_amount": "449.82", + "total_payable": "2948.82" + }, + "payment_session": { + "payment_id": "pay_01HKZM8Z2B3C4D5E6F7G8H9I0J", + "razorpay_order_id": "order_NfJZ5mUg7MUlEe", + "razorpay_key": "rzp_test_1DP5mmOlF5G5ag", + "checkout_url": "https://checkout.razorpay.com/v1/checkout.js", + "expires_at": "2024-01-15T11:30:00Z" + }, + "idempotency_key": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9I_1705312200" +} +``` + +#### Step 3: Frontend Payment Processing + +```javascript +// Initialize Razorpay payment +const options = { + key: "rzp_test_1DP5mmOlF5G5ag", + order_id: "order_NfJZ5mUg7MUlEe", + amount: 294882, // in paise + currency: "INR", + name: "Your LMS Platform", + description: "Full Stack Web Development Bootcamp", + image: "https://yourapp.com/logo.png", + handler: function (response) { + // Payment successful + console.log("Payment ID:", response.razorpay_payment_id); + console.log("Order ID:", response.razorpay_order_id); + console.log("Signature:", response.razorpay_signature); + + // Verify payment on backend + verifyPayment(response); + }, + prefill: { + name: "John Doe", + email: "john@example.com", + contact: "9876543210", + }, + theme: { + color: "#3399cc", + }, + modal: { + ondismiss: function () { + console.log("Payment cancelled"); + handlePaymentCancellation(); + }, + }, +}; + +const rzp = new Razorpay(options); +rzp.open(); +``` + +#### Step 4: Payment Verification (Backend Webhook) + +```json +// Razorpay webhook payload +{ + "entity": "event", + "account_id": "acc_BFQ7uQEaa30GJJ", + "event": "payment.captured", + "contains": ["payment"], + "payload": { + "payment": { + "entity": "payment", + "id": "pay_NfJZ6mUg7MUlEf", + "amount": 294882, + "currency": "INR", + "status": "captured", + "order_id": "order_NfJZ5mUg7MUlEe", + "method": "card", + "description": "Full Stack Web Development Bootcamp", + "notes": { + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9I", + "course_id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P8", + "user_id": "usr_01HKZM8X0Z1Y2X3W4V5U6T7S8R" + }, + "created_at": 1705312800 + } + }, + "created_at": 1705312800 +} +``` + +#### Step 5: Check Enrollment Status + +```http +GET /api/v1/enrollments/enr_01HKZM8Y1A2B3C4D5E6F7G8H9I/status +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... +``` + +**Response:** + +```json +{ + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9I", + "enrollment_status": "enrolled", + "payment_status": "completed", + "payment_id": "pay_01HKZM8Z2B3C4D5E6F7G8H9I0J", + "razorpay_payment_id": "pay_NfJZ6mUg7MUlEf", + "completed_at": "2024-01-15T10:40:00Z", + "course_access": { + "has_access": true, + "access_granted_at": "2024-01-15T10:40:00Z", + "next_lesson_url": "/api/v1/courses/int_01HKZM7X9P0QJ2K3L4M5N6O7P8/lessons/1" + } +} +``` + +### Scenario 2: Free Course Enrollment + +#### Initialize Free Course Enrollment + +```http +POST /api/v1/enrollments/initialize +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... +Content-Type: application/json + +{ + "course_id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P9", + "success_url": "https://myapp.com/enrollment/success", + "cancel_url": "https://myapp.com/enrollment/cancel" +} +``` + +**Response:** + +```json +{ + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9J", + "status": "enrolled", + "payment_required": false, + "course": { + "id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P9", + "title": "Introduction to Programming" + }, + "pricing": { + "original_amount": "0.00", + "discount_amount": "0.00", + "final_amount": "0.00", + "currency": "INR", + "tax_amount": "0.00", + "total_payable": "0.00" + }, + "course_access": { + "has_access": true, + "access_granted_at": "2024-01-15T10:30:00Z", + "next_lesson_url": "/api/v1/courses/int_01HKZM7X9P0QJ2K3L4M5N6O7P9/lessons/1" + } +} +``` + +## ๐Ÿšจ Error Handling Examples + +### Error 1: Already Enrolled + +```http +POST /api/v1/enrollments/initialize +``` + +**Response (409 Conflict):** + +```json +{ + "error": { + "code": "already_enrolled", + "message": "User is already enrolled in this course", + "details": { + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9K", + "enrollment_status": "enrolled", + "enrolled_at": "2024-01-10T14:30:00Z" + }, + "actions": [ + { + "type": "redirect", + "url": "/api/v1/courses/int_01HKZM7X9P0QJ2K3L4M5N6O7P8/access", + "label": "Continue Learning" + } + ] + } +} +``` + +### Error 2: Course Not Available + +```http +POST /api/v1/enrollments/initialize +``` + +**Response (422 Unprocessable Entity):** + +```json +{ + "error": { + "code": "course_not_available", + "message": "Course is not available for enrollment", + "details": { + "course_id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P8", + "status": "draft", + "available_from": "2024-02-01T00:00:00Z" + }, + "actions": [ + { + "type": "notify", + "label": "Notify me when available" + } + ] + } +} +``` + +### Error 3: Payment Failed + +```http +GET /api/v1/enrollments/enr_01HKZM8Y1A2B3C4D5E6F7G8H9I/status +``` + +**Response:** + +```json +{ + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9I", + "enrollment_status": "failed", + "payment_status": "failed", + "payment_id": "pay_01HKZM8Z2B3C4D5E6F7G8H9I0J", + "razorpay_payment_id": "pay_NfJZ6mUg7MUlEf", + "failed_at": "2024-01-15T10:35:00Z", + "error": { + "code": "payment_failed", + "message": "Payment was declined by the bank", + "razorpay_error": "BAD_REQUEST_ERROR", + "retry_options": [ + { + "type": "retry_payment", + "url": "/api/v1/enrollments/enr_01HKZM8Y1A2B3C4D5E6F7G8H9I/retry", + "expires_at": "2024-01-16T10:35:00Z" + }, + { + "type": "change_payment_method", + "url": "/api/v1/enrollments/enr_01HKZM8Y1A2B3C4D5E6F7G8H9I/update-payment" + } + ] + } +} +``` + +## ๐Ÿ”„ Advanced Scenarios + +### Scenario 3: Coupon Application + +#### Apply Coupon Code + +```http +POST /api/v1/coupons/validate +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... +Content-Type: application/json + +{ + "coupon_code": "EARLY50", + "course_id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P8", + "user_id": "usr_01HKZM8X0Z1Y2X3W4V5U6T7S8R" +} +``` + +**Response:** + +```json +{ + "valid": true, + "coupon": { + "code": "EARLY50", + "type": "percentage", + "value": "20.00", + "description": "Early Bird 20% Discount", + "applicable_courses": ["int_01HKZM7X9P0QJ2K3L4M5N6O7P8"], + "expires_at": "2024-02-29T23:59:59Z", + "usage_left": 89 + }, + "discount_calculation": { + "original_amount": "4999.00", + "coupon_discount": "999.80", + "other_discounts": "2000.00", + "total_discount": "2999.80", + "final_amount": "1999.20" + } +} +``` + +### Scenario 4: Bulk Enrollment (Enterprise) + +#### Initialize Bulk Enrollment + +```http +POST /api/v1/enrollments/bulk/initialize +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... +Content-Type: application/json + +{ + "course_id": "int_01HKZM7X9P0QJ2K3L4M5N6O7P8", + "user_emails": [ + "employee1@company.com", + "employee2@company.com", + "employee3@company.com" + ], + "billing_details": { + "company_name": "Acme Corp", + "billing_email": "billing@acmecorp.com", + "purchase_order": "PO-2024-001", + "payment_terms": "net_30" + }, + "success_url": "https://acmecorp.com/enrollment/success", + "cancel_url": "https://acmecorp.com/enrollment/cancel" +} +``` + +**Response:** + +```json +{ + "bulk_enrollment_id": "bulk_01HKZM8Y1A2B3C4D5E6F7G8H9K", + "status": "pending", + "enrollments": [ + { + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9L", + "user_email": "employee1@company.com", + "status": "pending" + }, + { + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9M", + "user_email": "employee2@company.com", + "status": "pending" + }, + { + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9N", + "user_email": "employee3@company.com", + "status": "pending" + } + ], + "pricing": { + "per_user_amount": "2999.00", + "user_count": 3, + "subtotal": "8997.00", + "bulk_discount": "899.70", + "tax_amount": "1457.55", + "total_amount": "9554.85", + "currency": "INR" + }, + "invoice": { + "invoice_id": "inv_01HKZM8Y1A2B3C4D5E6F7G8H9O", + "invoice_url": "/api/v1/invoices/inv_01HKZM8Y1A2B3C4D5E6F7G8H9O/download", + "due_date": "2024-02-14T23:59:59Z" + } +} +``` + +### Scenario 5: Payment Retry + +#### Retry Failed Payment + +```http +POST /api/v1/enrollments/enr_01HKZM8Y1A2B3C4D5E6F7G8H9I/retry +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... +Content-Type: application/json + +{ + "payment_method_preference": "razorpay", + "retry_reason": "user_requested" +} +``` + +**Response:** + +```json +{ + "enrollment_id": "enr_01HKZM8Y1A2B3C4D5E6F7G8H9I", + "status": "pending_payment", + "retry_attempt": 2, + "payment_session": { + "payment_id": "pay_01HKZM8Z2B3C4D5E6F7G8H9I0K", + "razorpay_order_id": "order_NfJZ7mUg7MUlEg", + "razorpay_key": "rzp_test_1DP5mmOlF5G5ag", + "expires_at": "2024-01-15T12:30:00Z" + }, + "pricing": { + "final_amount": "2948.82", + "currency": "INR" + } +} +``` + +## ๐Ÿงช Test Scenarios + +### Test Case 1: Complete Happy Path + +```javascript +// Test: Successful paid course enrollment +async function testPaidCourseEnrollment() { + // 1. Get course info + const courseResponse = await api.get("/api/v1/internships/int_test123"); + expect(courseResponse.status).toBe(200); + expect(courseResponse.data.internship.price).toBe("4999.00"); + + // 2. Initialize enrollment + const enrollmentResponse = await api.post("/api/v1/enrollments/initialize", { + course_id: "int_test123", + success_url: "http://test.com/success", + cancel_url: "http://test.com/cancel", + }); + + expect(enrollmentResponse.status).toBe(200); + expect(enrollmentResponse.data.payment_required).toBe(true); + expect( + enrollmentResponse.data.payment_session.razorpay_order_id + ).toBeDefined(); + + // 3. Simulate successful payment webhook + const webhookPayload = createSuccessfulPaymentWebhook( + enrollmentResponse.data.payment_session.razorpay_order_id, + enrollmentResponse.data.enrollment_id + ); + + await simulateWebhook("/webhooks/razorpay", webhookPayload); + + // 4. Verify enrollment completion + const statusResponse = await api.get( + `/api/v1/enrollments/${enrollmentResponse.data.enrollment_id}/status` + ); + + expect(statusResponse.data.enrollment_status).toBe("enrolled"); + expect(statusResponse.data.course_access.has_access).toBe(true); +} +``` + +### Test Case 2: Free Course Enrollment + +```javascript +async function testFreeCourseEnrollment() { + const response = await api.post("/api/v1/enrollments/initialize", { + course_id: "int_free123", + success_url: "http://test.com/success", + cancel_url: "http://test.com/cancel", + }); + + expect(response.status).toBe(200); + expect(response.data.payment_required).toBe(false); + expect(response.data.status).toBe("enrolled"); +} +``` + +### Test Case 3: Duplicate Enrollment + +```javascript +async function testDuplicateEnrollment() { + // First enrollment + await api.post("/api/v1/enrollments/initialize", { + course_id: "int_test123", + }); + + // Second enrollment attempt + const response = await api.post("/api/v1/enrollments/initialize", { + course_id: "int_test123", + }); + + expect(response.status).toBe(409); + expect(response.data.error.code).toBe("already_enrolled"); +} +``` + +### Test Case 4: Payment Failure Recovery + +```javascript +async function testPaymentFailureRecovery() { + // Initialize enrollment + const enrollmentResponse = await api.post("/api/v1/enrollments/initialize", { + course_id: "int_test123", + }); + + // Simulate failed payment webhook + const failureWebhook = createFailedPaymentWebhook( + enrollmentResponse.data.payment_session.razorpay_order_id, + enrollmentResponse.data.enrollment_id + ); + + await simulateWebhook("/webhooks/razorpay", failureWebhook); + + // Check failure status + const statusResponse = await api.get( + `/api/v1/enrollments/${enrollmentResponse.data.enrollment_id}/status` + ); + + expect(statusResponse.data.enrollment_status).toBe("failed"); + expect(statusResponse.data.error.retry_options).toBeDefined(); + + // Retry payment + const retryResponse = await api.post( + `/api/v1/enrollments/${enrollmentResponse.data.enrollment_id}/retry` + ); + + expect(retryResponse.status).toBe(200); + expect(retryResponse.data.payment_session.razorpay_order_id).toBeDefined(); +} +``` + +## ๐Ÿ“Š Performance Testing + +### Load Test Scenarios + +#### 1. Concurrent Enrollments + +```javascript +// Test: 100 concurrent enrollment requests +const concurrentEnrollments = Array.from({ length: 100 }, (_, i) => + api.post("/api/v1/enrollments/initialize", { + course_id: "int_popular123", + user_id: `usr_test${i}`, + success_url: "http://test.com/success", + cancel_url: "http://test.com/cancel", + }) +); + +const results = await Promise.allSettled(concurrentEnrollments); +const successCount = results.filter((r) => r.status === "fulfilled").length; +const averageResponseTime = calculateAverageResponseTime(results); + +expect(successCount).toBeGreaterThan(95); // 95% success rate +expect(averageResponseTime).toBeLessThan(500); // Under 500ms +``` + +#### 2. Webhook Processing Load + +```javascript +// Test: Process 1000 webhook events in parallel +const webhookEvents = Array.from({ length: 1000 }, (_, i) => + createPaymentWebhook(`order_${i}`, `enr_${i}`) +); + +const startTime = Date.now(); +const results = await Promise.allSettled( + webhookEvents.map((event) => simulateWebhook("/webhooks/razorpay", event)) +); +const processingTime = Date.now() - startTime; + +expect(results.filter((r) => r.status === "fulfilled").length).toBe(1000); +expect(processingTime).toBeLessThan(10000); // Under 10 seconds +``` + +## ๐Ÿ”’ Security Testing + +### Security Test Cases + +#### 1. Webhook Signature Validation + +```javascript +async function testWebhookSignatureValidation() { + const validWebhook = createValidWebhook(); + const invalidWebhook = { ...validWebhook }; + delete invalidWebhook.headers["x-razorpay-signature"]; + + // Valid webhook should be processed + const validResponse = await simulateWebhook( + "/webhooks/razorpay", + validWebhook + ); + expect(validResponse.status).toBe(200); + + // Invalid webhook should be rejected + const invalidResponse = await simulateWebhook( + "/webhooks/razorpay", + invalidWebhook + ); + expect(invalidResponse.status).toBe(401); +} +``` + +#### 2. Idempotency Testing + +```javascript +async function testIdempotency() { + const enrollmentRequest = { + course_id: "int_test123", + idempotency_key: "test_idempotency_123", + }; + + // First request + const response1 = await api.post( + "/api/v1/enrollments/initialize", + enrollmentRequest + ); + expect(response1.status).toBe(200); + + // Duplicate request with same idempotency key + const response2 = await api.post( + "/api/v1/enrollments/initialize", + enrollmentRequest + ); + expect(response2.status).toBe(200); + expect(response2.data.enrollment_id).toBe(response1.data.enrollment_id); +} +``` + +This comprehensive API examples document provides practical request/response samples, error handling patterns, and test scenarios to help implement and validate the enrollment workflow system. diff --git a/docs/prds/ENROLLMENT_IMPLEMENTATION_GUIDE.md b/docs/prds/ENROLLMENT_IMPLEMENTATION_GUIDE.md new file mode 100644 index 0000000..e01f426 --- /dev/null +++ b/docs/prds/ENROLLMENT_IMPLEMENTATION_GUIDE.md @@ -0,0 +1,696 @@ +# ๐Ÿ› ๏ธ LMS Enrollment Workflow - Implementation Guide + +## ๐Ÿ“‹ Overview + +This guide provides step-by-step implementation instructions for the LMS enrollment workflow with Razorpay payment integration, building upon your existing architecture. + +## ๐Ÿ—๏ธ Implementation Phases + +### Phase 1: Database Schema Updates + +#### 1.1 Update Enrollment Schema + +Your existing enrollment schema is mostly complete. Add these enhancements: + +```sql +-- Add indexes for performance +CREATE INDEX CONCURRENTLY idx_enrollments_user_course ON enrollments(user_id, internship_id) WHERE status != 'deleted'; +CREATE INDEX CONCURRENTLY idx_enrollments_payment_lookup ON enrollments(payment_id) WHERE payment_id IS NOT NULL; + +-- Add enrollment expiry support +ALTER TABLE enrollments ADD COLUMN enrollment_expires_at TIMESTAMP; +``` + +### Phase 2: Enhanced Service Implementation + +#### 2.1 Create Enrollment Service + +```go +// File: internal/service/enrollment.go +package service + +import ( + "context" + "fmt" + "time" + + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/domain/enrollment" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" + "github.com/shopspring/decimal" +) + +type EnrollmentService interface { + InitializeEnrollment(ctx context.Context, req *dto.InitializeEnrollmentRequest) (*dto.EnrollmentResponse, error) + CompleteEnrollment(ctx context.Context, enrollmentID string) error + GetEnrollmentStatus(ctx context.Context, enrollmentID string) (*dto.EnrollmentStatusResponse, error) + CancelEnrollment(ctx context.Context, enrollmentID string, reason string) error +} + +type enrollmentService struct { + ServiceParams ServiceParams + PaymentService PaymentService +} + +func NewEnrollmentService(params ServiceParams, paymentSvc PaymentService) EnrollmentService { + return &enrollmentService{ + ServiceParams: params, + PaymentService: paymentSvc, + } +} + +func (s *enrollmentService) InitializeEnrollment(ctx context.Context, req *dto.InitializeEnrollmentRequest) (*dto.EnrollmentResponse, error) { + // 1. Validate request + if err := s.validateEnrollmentRequest(ctx, req); err != nil { + return nil, err + } + + // 2. Check existing enrollment + existingEnrollment, err := s.checkExistingEnrollment(ctx, req.UserID, req.CourseID) + if err != nil { + return nil, err + } + if existingEnrollment != nil { + return s.handleExistingEnrollment(ctx, existingEnrollment) + } + + // 3. Get course details + course, err := s.ServiceParams.InternshipRepo.Get(ctx, req.CourseID) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Course not found"). + Mark(ierr.ErrNotFound) + } + + // 4. Calculate pricing + pricing := s.calculatePricing(course, req.CouponCode) + + var response *dto.EnrollmentResponse + + // 5. Execute in transaction + err = s.ServiceParams.DB.WithTx(ctx, func(ctx context.Context) error { + // Create enrollment record + enrollment, err := s.createEnrollmentRecord(ctx, req.UserID, req.CourseID, pricing) + if err != nil { + return err + } + + // Handle free courses + if pricing.FinalAmount.IsZero() { + return s.handleFreeCourse(ctx, enrollment) + } + + // Create payment for paid courses + paymentResp, err := s.createPaymentOrder(ctx, enrollment, pricing, req) + if err != nil { + return err + } + + response = &dto.EnrollmentResponse{ + EnrollmentID: enrollment.ID, + Status: string(enrollment.EnrollmentStatus), + PaymentRequired: true, + Pricing: pricing, + PaymentSession: paymentResp.PaymentSession, + } + + return nil + }) + + return response, err +} +``` + +#### 2.2 Create DTOs + +```go +// File: internal/api/dto/enrollment.go +package dto + +import ( + "time" + "github.com/shopspring/decimal" +) + +type InitializeEnrollmentRequest struct { + CourseID string `json:"course_id" validate:"required"` + UserID string `json:"-"` // Set from context + CouponCode string `json:"coupon_code,omitempty"` + SuccessURL string `json:"success_url" validate:"required,url"` + CancelURL string `json:"cancel_url" validate:"required,url"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +type EnrollmentResponse struct { + EnrollmentID string `json:"enrollment_id"` + Status string `json:"status"` + PaymentRequired bool `json:"payment_required"` + Pricing *PricingInfo `json:"pricing"` + PaymentSession *PaymentSessionInfo `json:"payment_session,omitempty"` +} + +type PricingInfo struct { + OriginalAmount decimal.Decimal `json:"original_amount"` + DiscountAmount decimal.Decimal `json:"discount_amount"` + FinalAmount decimal.Decimal `json:"final_amount"` + Currency string `json:"currency"` + TaxAmount decimal.Decimal `json:"tax_amount"` + TotalPayable decimal.Decimal `json:"total_payable"` +} + +type PaymentSessionInfo struct { + PaymentID string `json:"payment_id"` + RazorpayOrderID string `json:"razorpay_order_id"` + RazorpayKey string `json:"razorpay_key"` + PaymentURL string `json:"payment_url,omitempty"` + ExpiresAt time.Time `json:"expires_at"` +} + +type EnrollmentStatusResponse struct { + EnrollmentID string `json:"enrollment_id"` + EnrollmentStatus string `json:"enrollment_status"` + PaymentStatus string `json:"payment_status"` + PaymentID string `json:"payment_id,omitempty"` + RazorpayPaymentID string `json:"razorpay_payment_id,omitempty"` + CompletedAt *time.Time `json:"completed_at,omitempty"` + CourseAccessURL string `json:"course_access_url,omitempty"` +} +``` + +### Phase 3: Enhanced Razorpay Integration + +#### 3.1 Extend Razorpay Provider + +```go +// File: internal/payment/providers/razorpay.go +// Add these methods to existing RazorpayProvider + +func (r *RazorpayProvider) CreateEnrollmentOrder(ctx context.Context, req *dto.EnrollmentPaymentRequest) (*dto.PaymentResponse, error) { + // Convert amount to paise + amountInPaise := int(req.Amount.Mul(decimal.NewFromInt(100)).IntPart()) + + orderData := map[string]interface{}{ + "amount": amountInPaise, + "currency": req.Currency, + "receipt": req.IdempotencyKey, + "payment_capture": 1, + "notes": map[string]string{ + "enrollment_id": req.EnrollmentID, + "course_id": req.CourseID, + "user_id": req.UserID, + }, + } + + order, err := r.razorpayClient.Order.Create(orderData, nil) + if err != nil { + return nil, fmt.Errorf("razorpay order creation failed: %w", err) + } + + return &dto.PaymentResponse{ + Payment: payment.Payment{ + ID: order["id"].(string), + }, + GatewayResponse: &dto.PaymentGatewayResponse{ + ProviderPaymentID: order["id"].(string), + RedirectURL: r.generateCheckoutURL(order["id"].(string)), + Status: "created", + Raw: order, + }, + }, nil +} + +func (r *RazorpayProvider) generateCheckoutURL(orderID string) string { + // Return hosted checkout URL or return empty for custom integration + return fmt.Sprintf("https://checkout.razorpay.com/v1/checkout.js?order_id=%s", orderID) +} +``` + +#### 3.2 Enhanced Webhook Processing + +```go +// File: internal/webhook/subscriber/enrollment.go +package subscriber + +import ( + "context" + "encoding/json" + + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/service" + "github.com/omkar273/codegeeky/internal/types" +) + +type EnrollmentWebhookSubscriber struct { + enrollmentSvc service.EnrollmentService + paymentSvc service.PaymentService +} + +func NewEnrollmentWebhookSubscriber(enrollmentSvc service.EnrollmentService, paymentSvc service.PaymentService) *EnrollmentWebhookSubscriber { + return &EnrollmentWebhookSubscriber{ + enrollmentSvc: enrollmentSvc, + paymentSvc: paymentSvc, + } +} + +func (s *EnrollmentWebhookSubscriber) HandlePaymentSuccess(ctx context.Context, event *dto.WebhookResult) error { + // Extract enrollment ID from payment metadata + enrollmentID, ok := event.Payload["enrollment_id"].(string) + if !ok { + return fmt.Errorf("enrollment_id not found in payment webhook") + } + + // Update payment status + updateReq := &dto.UpdatePaymentRequest{ + PaymentStatus: &types.PaymentStatusSuccess, + GatewayPaymentID: &event.PaymentID, + } + + if _, err := s.paymentSvc.Update(ctx, event.PaymentID, updateReq); err != nil { + return fmt.Errorf("failed to update payment: %w", err) + } + + // Complete enrollment + return s.enrollmentSvc.CompleteEnrollment(ctx, enrollmentID) +} + +func (s *EnrollmentWebhookSubscriber) HandlePaymentFailure(ctx context.Context, event *dto.WebhookResult) error { + enrollmentID, ok := event.Payload["enrollment_id"].(string) + if !ok { + return fmt.Errorf("enrollment_id not found in payment webhook") + } + + // Update payment status + updateReq := &dto.UpdatePaymentRequest{ + PaymentStatus: &types.PaymentStatusFailed, + GatewayPaymentID: &event.PaymentID, + ErrorMessage: &event.Reason, + } + + if _, err := s.paymentSvc.Update(ctx, event.PaymentID, updateReq); err != nil { + return fmt.Errorf("failed to update payment: %w", err) + } + + // Cancel enrollment + return s.enrollmentSvc.CancelEnrollment(ctx, enrollmentID, "payment_failed") +} +``` + +### Phase 4: API Endpoints + +#### 4.1 Enrollment Controller + +```go +// File: internal/api/v1/enrollment.go +package v1 + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/service" + "github.com/omkar273/codegeeky/internal/types" +) + +type EnrollmentHandler struct { + enrollmentSvc service.EnrollmentService +} + +func NewEnrollmentHandler(enrollmentSvc service.EnrollmentService) *EnrollmentHandler { + return &EnrollmentHandler{enrollmentSvc: enrollmentSvc} +} + +// @Summary Initialize course enrollment +// @Description Start the enrollment process for a course +// @Tags enrollments +// @Accept json +// @Produce json +// @Param request body dto.InitializeEnrollmentRequest true "Enrollment request" +// @Success 200 {object} dto.EnrollmentResponse +// @Router /enrollments/initialize [post] +func (h *EnrollmentHandler) InitializeEnrollment(c *gin.Context) { + var req dto.InitializeEnrollmentRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Set user ID from context + req.UserID = types.GetUserID(c) + + response, err := h.enrollmentSvc.InitializeEnrollment(c, &req) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, response) +} + +// @Summary Get enrollment status +// @Description Get the current status of an enrollment +// @Tags enrollments +// @Produce json +// @Param enrollmentId path string true "Enrollment ID" +// @Success 200 {object} dto.EnrollmentStatusResponse +// @Router /enrollments/{enrollmentId}/status [get] +func (h *EnrollmentHandler) GetEnrollmentStatus(c *gin.Context) { + enrollmentID := c.Param("enrollmentId") + + response, err := h.enrollmentSvc.GetEnrollmentStatus(c, enrollmentID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, response) +} +``` + +#### 4.2 Router Setup + +```go +// File: internal/api/router.go +// Add to existing router setup + +func (r *Router) setupEnrollmentRoutes() { + enrollmentGroup := r.engine.Group("/api/v1/enrollments") + enrollmentGroup.Use(r.middleware.AuthRequired()) + + enrollmentHandler := v1.NewEnrollmentHandler(r.services.EnrollmentService()) + + enrollmentGroup.POST("/initialize", enrollmentHandler.InitializeEnrollment) + enrollmentGroup.GET("/:enrollmentId/status", enrollmentHandler.GetEnrollmentStatus) +} +``` + +### Phase 5: Frontend Integration + +#### 5.1 JavaScript/TypeScript Integration + +```typescript +// enrollment.service.ts +export interface EnrollmentRequest { + course_id: string; + coupon_code?: string; + success_url: string; + cancel_url: string; + metadata?: Record; +} + +export interface EnrollmentResponse { + enrollment_id: string; + status: string; + payment_required: boolean; + pricing: PricingInfo; + payment_session?: PaymentSessionInfo; +} + +export class EnrollmentService { + constructor(private apiClient: ApiClient) {} + + async initializeEnrollment( + request: EnrollmentRequest + ): Promise { + const response = await this.apiClient.post( + "/api/v1/enrollments/initialize", + request + ); + return response.data; + } + + async getEnrollmentStatus( + enrollmentId: string + ): Promise { + const response = await this.apiClient.get( + `/api/v1/enrollments/${enrollmentId}/status` + ); + return response.data; + } + + async processRazorpayPayment( + enrollmentData: EnrollmentResponse + ): Promise { + return new Promise((resolve, reject) => { + const options = { + key: enrollmentData.payment_session!.razorpay_key, + order_id: enrollmentData.payment_session!.razorpay_order_id, + amount: Math.round(enrollmentData.pricing.total_payable * 100), + currency: enrollmentData.pricing.currency, + name: "Your Platform Name", + description: "Course Enrollment", + handler: async (response: any) => { + try { + await this.verifyPayment({ + razorpay_payment_id: response.razorpay_payment_id, + razorpay_order_id: response.razorpay_order_id, + razorpay_signature: response.razorpay_signature, + enrollment_id: enrollmentData.enrollment_id, + }); + resolve(); + } catch (error) { + reject(error); + } + }, + modal: { + ondismiss: () => reject(new Error("Payment cancelled")), + }, + }; + + const rzp = new (window as any).Razorpay(options); + rzp.open(); + }); + } + + private async verifyPayment(verificationData: any): Promise { + await this.apiClient.post("/api/v1/payments/verify", verificationData); + } +} +``` + +#### 5.2 React Component Example + +```tsx +// EnrollmentButton.tsx +import React, { useState } from "react"; +import { EnrollmentService } from "./enrollment.service"; + +interface EnrollmentButtonProps { + courseId: string; + courseName: string; + price: number; +} + +export const EnrollmentButton: React.FC = ({ + courseId, + courseName, + price, +}) => { + const [loading, setLoading] = useState(false); + const [enrollmentStatus, setEnrollmentStatus] = useState(""); + const enrollmentService = new EnrollmentService(); + + const handleEnrollment = async () => { + setLoading(true); + try { + // Initialize enrollment + const enrollmentResponse = await enrollmentService.initializeEnrollment({ + course_id: courseId, + success_url: `${window.location.origin}/enrollment/success`, + cancel_url: `${window.location.origin}/enrollment/cancel`, + }); + + if (!enrollmentResponse.payment_required) { + // Free course - enrollment complete + setEnrollmentStatus("enrolled"); + return; + } + + // Process payment + await enrollmentService.processRazorpayPayment(enrollmentResponse); + + // Check final status + const statusResponse = await enrollmentService.getEnrollmentStatus( + enrollmentResponse.enrollment_id + ); + setEnrollmentStatus(statusResponse.enrollment_status); + } catch (error) { + console.error("Enrollment failed:", error); + alert("Enrollment failed. Please try again."); + } finally { + setLoading(false); + } + }; + + if (enrollmentStatus === "enrolled") { + return ; + } + + return ( + + ); +}; +``` + +## ๐Ÿงช Testing Strategy + +### Integration Tests + +```go +// File: tests/integration/enrollment_test.go +package integration + +import ( + "testing" + "context" + + "github.com/stretchr/testify/assert" + "github.com/omkar273/codegeeky/internal/api/dto" +) + +func TestEnrollmentWorkflow(t *testing.T) { + // Setup test environment + testEnv := setupTestEnvironment(t) + defer testEnv.Cleanup() + + // Test data + userID := "usr_test123" + courseID := "int_test123" + + t.Run("Successful Paid Course Enrollment", func(t *testing.T) { + // Initialize enrollment + req := &dto.InitializeEnrollmentRequest{ + CourseID: courseID, + UserID: userID, + SuccessURL: "http://test.com/success", + CancelURL: "http://test.com/cancel", + } + + response, err := testEnv.EnrollmentService.InitializeEnrollment(context.Background(), req) + assert.NoError(t, err) + assert.True(t, response.PaymentRequired) + assert.NotEmpty(t, response.EnrollmentID) + + // Simulate successful payment webhook + webhookEvent := createPaymentSuccessWebhook(response.PaymentSession.PaymentID, response.EnrollmentID) + err = testEnv.WebhookService.ProcessWebhook(context.Background(), webhookEvent) + assert.NoError(t, err) + + // Verify enrollment completion + status, err := testEnv.EnrollmentService.GetEnrollmentStatus(context.Background(), response.EnrollmentID) + assert.NoError(t, err) + assert.Equal(t, "enrolled", status.EnrollmentStatus) + }) + + t.Run("Free Course Enrollment", func(t *testing.T) { + // Create free course + freeCourseID := createFreeCourse(t, testEnv) + + req := &dto.InitializeEnrollmentRequest{ + CourseID: freeCourseID, + UserID: userID, + SuccessURL: "http://test.com/success", + CancelURL: "http://test.com/cancel", + } + + response, err := testEnv.EnrollmentService.InitializeEnrollment(context.Background(), req) + assert.NoError(t, err) + assert.False(t, response.PaymentRequired) + + // Verify immediate enrollment + status, err := testEnv.EnrollmentService.GetEnrollmentStatus(context.Background(), response.EnrollmentID) + assert.NoError(t, err) + assert.Equal(t, "enrolled", status.EnrollmentStatus) + }) +} +``` + +## ๐Ÿ“Š Monitoring & Metrics + +### Key Metrics to Track + +```go +// File: internal/metrics/enrollment.go +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +var ( + EnrollmentInitiations = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "enrollment_initiations_total", + Help: "Total number of enrollment initiations", + }, + []string{"course_id", "payment_required"}, + ) + + EnrollmentCompletions = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "enrollment_completions_total", + Help: "Total number of successful enrollments", + }, + []string{"course_id", "payment_method"}, + ) + + PaymentProcessingDuration = promauto.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "payment_processing_duration_seconds", + Help: "Time taken to process payments", + }, + []string{"gateway", "status"}, + ) + + EnrollmentConversionRate = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "enrollment_conversion_rate", + Help: "Enrollment conversion rate by course", + }, + []string{"course_id"}, + ) +) +``` + +## ๐Ÿš€ Deployment Checklist + +- [ ] Database migrations executed +- [ ] Environment variables configured +- [ ] Razorpay credentials set up +- [ ] Webhook endpoints configured +- [ ] Monitoring dashboards deployed +- [ ] Load testing completed +- [ ] Security audit passed +- [ ] Documentation updated + +## ๐Ÿ” Troubleshooting Guide + +### Common Issues + +1. **Payment Webhook Not Received** + + - Check webhook URL accessibility + - Verify webhook signature validation + - Check firewall rules + +2. **Enrollment Status Not Updating** + + - Check database transactions + - Verify webhook processing logic + - Check for race conditions + +3. **Razorpay Order Creation Fails** + - Verify API credentials + - Check amount formatting (paise conversion) + - Validate request parameters + +This implementation guide provides a practical roadmap for building the enrollment workflow on top of your existing architecture. Each phase builds incrementally, allowing for testing and validation at each step. diff --git a/docs/prds/ENROLLMENT_PAYMENT_SYSTEM_PRD.md b/docs/prds/ENROLLMENT_PAYMENT_SYSTEM_PRD.md new file mode 100644 index 0000000..3faaa5b --- /dev/null +++ b/docs/prds/ENROLLMENT_PAYMENT_SYSTEM_PRD.md @@ -0,0 +1,758 @@ +# ๐Ÿš€ Internship Enrollment Workflow with Payment Integration - PRD + +## ๐Ÿ“‹ Executive Summary + +This document outlines the Product Requirements Document (PRD) for implementing a comprehensive internship enrollment workflow with flexible payment integration, inspired by best practices from platforms like Udemy, Coursera, and LinkedIn Learning. + +### ๐ŸŽฏ Vision + +Create a scalable, flexible payment system that supports multiple payment methods, gateways, and business models while providing excellent user experience for internship enrollments. + +--- + +## ๐Ÿ” Market Research Analysis + +Based on research of major learning platforms: + +### Platform Analysis + +#### **Udemy's Enrollment Model** + +- **Payment Methods**: Credit/Debit cards, PayPal, Apple Pay, Google Pay +- **Pricing Strategy**: Fixed course prices with frequent discount campaigns +- **Coupons & Discounts**: Instructor coupons, site-wide promotions, bulk discounts +- **Refund Policy**: 30-day money-back guarantee +- **Currency Support**: 190+ countries with local pricing + +#### **Coursera's Subscription Model** + +- **Payment Methods**: Credit cards, PayPal, bank transfers (select regions) +- **Pricing Models**: + - Individual course purchases + - Coursera Plus subscription ($59/month or $399/year) + - University partnerships +- **Financial Aid**: Need-based assistance program +- **Corporate Sales**: B2B enterprise solutions + +#### **LinkedIn Learning's Approach** + +- **Payment Integration**: Seamless with LinkedIn Premium +- **Business Model**: Subscription-based with enterprise licensing +- **Gift Subscriptions**: Corporate gifting options +- **Regional Pricing**: Localized pricing strategies + +### Key Insights for Our System + +1. **Flexibility is King**: Support multiple payment methods and business models +2. **Discount Strategy**: Robust coupon and promotional system +3. **Global Reach**: Multi-currency and regional payment method support +4. **Enterprise Focus**: B2B payment solutions for corporate clients +5. **User Experience**: Seamless, secure, and intuitive payment flow + +--- + +## ๐ŸŽฏ Product Objectives + +### Primary Goals + +1. **Flexible Payment Architecture**: Support multiple payment gateways through unified interface +2. **Comprehensive Payment Methods**: Credit/debit cards, UPI, bank transfers, digital wallets, gift cards, offline payments +3. **Advanced Discount System**: Coupons, gift cards, bulk discounts, early bird pricing +4. **Enterprise Features**: Subscription management, invoicing, reporting +5. **Global Scalability**: Multi-currency, multi-region support + +### Success Metrics + +- **Payment Success Rate**: > 95% +- **Payment Processing Time**: < 3 seconds average +- **User Abandonment Rate**: < 10% at payment step +- **Gateway Uptime**: 99.9% availability +- **Refund Processing**: < 24 hours + +--- + +## ๐Ÿ‘ฅ User Personas + +### 1. **Individual Learner (Primary)** + +- **Demographics**: 18-35 years, tech-savvy, price-conscious +- **Payment Preferences**: Credit cards, UPI, digital wallets +- **Pain Points**: Complex checkout, hidden fees, payment failures +- **Goals**: Quick, secure, transparent payment process + +### 2. **Corporate Buyer (Secondary)** + +- **Demographics**: HR managers, L&D professionals +- **Payment Preferences**: Bank transfers, invoicing, bulk payments +- **Pain Points**: Manual processes, lack of reporting, compliance issues +- **Goals**: Bulk enrollments, detailed reporting, budget management + +### 3. **International Student (Tertiary)** + +- **Demographics**: Global audience, varying payment preferences +- **Payment Preferences**: Local payment methods, alternative currencies +- **Pain Points**: Currency conversion, payment method availability +- **Goals**: Access to local payment options, transparent pricing + +--- + +## ๐Ÿ”ง Functional Requirements + +### 1. **Payment Gateway Management System** + +#### **Core Features** + +- **Gateway Registration**: Add/remove payment gateways dynamically +- **Configuration Management**: API keys, webhook URLs, environment settings +- **Health Monitoring**: Gateway status monitoring and failover +- **Load Balancing**: Distribute traffic across multiple gateways +- **Compliance**: PCI DSS, regional compliance requirements + +#### **Supported Payment Gateways** + +- **Primary**: Stripe, Razorpay, PayPal +- **Regional**: Paytm, PhonePe, Google Pay, Apple Pay +- **Enterprise**: Wire transfers, ACH, SEPA +- **Emerging**: Cryptocurrency (Bitcoin, Ethereum) + +### 2. **Payment Methods Support** + +#### **Digital Payments** + +- **Credit/Debit Cards**: Visa, Mastercard, American Express, RuPay +- **Digital Wallets**: PayPal, Apple Pay, Google Pay, Amazon Pay +- **UPI**: All UPI-enabled apps (GPay, PhonePe, Paytm) +- **Net Banking**: Major banks integration +- **Buy Now, Pay Later**: Klarna, Afterpay, Simpl + +#### **Alternative Payment Methods** + +- **Bank Transfers**: Direct bank transfer, wire transfer +- **Offline Payments**: Cash deposits, demand drafts +- **Gift Cards**: Platform-specific gift cards +- **Cryptocurrency**: Bitcoin, Ethereum, stablecoins +- **Corporate**: Purchase orders, invoicing + +### 3. **Discount & Coupon System** + +#### **Coupon Types** + +- **Percentage Discounts**: 10%, 20%, 50% off +- **Fixed Amount**: โ‚น500 off, $50 off +- **Free Shipping**: Waive processing fees +- **BOGO**: Buy one, get one free +- **Tiered Discounts**: Bulk purchase discounts + +#### **Coupon Features** + +- **Usage Limits**: Per user, total usage, time-based +- **Stackability**: Multiple coupons per transaction +- **Auto-apply**: Best discount automatic application +- **Referral Codes**: Friend referral discounts +- **Seasonal Campaigns**: Holiday sales, special events + +### 4. **Subscription Management** + +#### **Subscription Types** + +- **Individual Plans**: Monthly, yearly subscriptions +- **Enterprise Plans**: Team subscriptions, unlimited access +- **Freemium Model**: Free tier with premium upgrades +- **Pay-per-course**: Individual course purchases + +#### **Subscription Features** + +- **Automatic Renewals**: Recurring billing management +- **Prorated Billing**: Mid-cycle plan changes +- **Dunning Management**: Failed payment retry logic +- **Cancellation**: Self-service cancellation with retention offers + +### 5. **Refund & Dispute Management** + +#### **Refund Types** + +- **Full Refunds**: Complete amount refund +- **Partial Refunds**: Partial amount refund +- **Credit Refunds**: Store credit instead of money +- **Automatic Refunds**: Policy-based automatic processing + +#### **Dispute Handling** + +- **Chargeback Management**: Automated dispute response +- **Evidence Collection**: Transaction evidence compilation +- **Fraud Detection**: ML-based fraud prevention +- **Reconciliation**: Automated settlement reconciliation + +### 6. **Invoicing & Billing** + +#### **Invoice Features** + +- **Auto-generation**: Automated invoice creation +- **Tax Calculation**: GST, VAT, sales tax computation +- **Multi-currency**: Invoices in local currencies +- **PDF Generation**: Professional invoice templates +- **Email Delivery**: Automated invoice distribution + +#### **Billing Analytics** + +- **Revenue Tracking**: Real-time revenue dashboards +- **Tax Reporting**: Compliance reporting +- **Subscription Analytics**: MRR, churn, LTV tracking +- **Financial Forecasting**: Predictive revenue modeling + +### 7. **Notification System** + +#### **Payment Notifications** + +- **SMS Alerts**: Payment confirmations, failures +- **Email Notifications**: Detailed payment receipts +- **Push Notifications**: Mobile app notifications +- **Webhook Events**: Real-time system integrations + +#### **Communication Channels** + +- **Transactional Emails**: Payment confirmations, receipts +- **Marketing Emails**: Promotional campaigns, offers +- **In-app Messages**: Payment status updates +- **WhatsApp Business**: Order confirmations (regional) + +### 8. **Webhook System** + +#### **Webhook Events** + +- **Payment Events**: Success, failure, pending +- **Subscription Events**: Created, updated, cancelled +- **Refund Events**: Initiated, completed, failed +- **Dispute Events**: Created, updated, resolved + +#### **Webhook Management** + +- **Endpoint Registration**: Multiple webhook endpoints +- **Retry Logic**: Exponential backoff retry +- **Signature Verification**: Webhook authenticity +- **Event Filtering**: Selective event delivery + +### 9. **Reporting & Analytics** + +#### **Financial Reports** + +- **Revenue Reports**: Daily, weekly, monthly revenue +- **Payment Method Analysis**: Performance by payment method +- **Gateway Performance**: Success rates, response times +- **Geographic Analysis**: Revenue by region/country + +#### **Operational Reports** + +- **Transaction Logs**: Detailed transaction history +- **Error Reports**: Payment failure analysis +- **Reconciliation Reports**: Settlement matching +- **Compliance Reports**: Tax, regulatory reporting + +--- + +## ๐Ÿ—๏ธ Technical Architecture + +### **System Architecture Overview** + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Client Applications โ”‚ +โ”‚ (Web, Mobile, Admin Dashboard) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ HTTP/REST API +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ API Gateway โ”‚ +โ”‚ (Authentication, Rate Limiting, Routing) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Payment Service Layer โ”‚ +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Enrollment โ”‚ โ”‚ Payment โ”‚ โ”‚ Billing โ”‚ โ”‚ +โ”‚ โ”‚ Service โ”‚ โ”‚ Service โ”‚ โ”‚ Service โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Discount โ”‚ โ”‚ Notification โ”‚ โ”‚ Webhook โ”‚ โ”‚ +โ”‚ โ”‚ Service โ”‚ โ”‚ Service โ”‚ โ”‚ Service โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Payment Gateway Layer โ”‚ +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Gateway โ”‚ โ”‚ Gateway โ”‚ โ”‚ Gateway โ”‚ โ”‚ +โ”‚ โ”‚ Manager โ”‚ โ”‚ Adapter โ”‚ โ”‚ Factory โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Stripe โ”‚ โ”‚ Razorpay โ”‚ โ”‚ PayPal โ”‚ โ”‚ +โ”‚ โ”‚ Adapter โ”‚ โ”‚ Adapter โ”‚ โ”‚ Adapter โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Data Layer โ”‚ +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ PostgreSQL โ”‚ โ”‚ Redis โ”‚ โ”‚ MongoDB โ”‚ โ”‚ +โ”‚ โ”‚ (Transactions) โ”‚ โ”‚ (Cache) โ”‚ โ”‚ (Logs) โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### **Design Patterns Implementation** + +#### **1. Strategy Pattern** - Payment Gateway Selection + +```go +type PaymentGateway interface { + ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) + ProcessRefund(ctx context.Context, req *RefundRequest) (*RefundResponse, error) + GetSupportedMethods() []PaymentMethod + ValidateWebhook(payload []byte, signature string) bool +} + +type PaymentGatewayManager struct { + gateways map[string]PaymentGateway + selector GatewaySelector +} + +func (m *PaymentGatewayManager) ProcessPayment(req *PaymentRequest) (*PaymentResponse, error) { + gateway := m.selector.SelectGateway(req) + return gateway.ProcessPayment(req.Context, req) +} +``` + +#### **2. Factory Pattern** - Gateway Creation + +```go +type GatewayFactory interface { + CreateGateway(gatewayType string, config GatewayConfig) (PaymentGateway, error) +} + +type ConcreteGatewayFactory struct{} + +func (f *ConcreteGatewayFactory) CreateGateway(gatewayType string, config GatewayConfig) (PaymentGateway, error) { + switch gatewayType { + case "stripe": + return NewStripeGateway(config) + case "razorpay": + return NewRazorpayGateway(config) + case "paypal": + return NewPayPalGateway(config) + default: + return nil, fmt.Errorf("unsupported gateway type: %s", gatewayType) + } +} +``` + +#### **3. Observer Pattern** - Webhook Processing + +```go +type PaymentEventObserver interface { + OnPaymentSuccess(event *PaymentEvent) + OnPaymentFailure(event *PaymentEvent) + OnRefundProcessed(event *RefundEvent) +} + +type PaymentEventPublisher struct { + observers []PaymentEventObserver +} + +func (p *PaymentEventPublisher) NotifyPaymentSuccess(event *PaymentEvent) { + for _, observer := range p.observers { + observer.OnPaymentSuccess(event) + } +} +``` + +#### **4. Decorator Pattern** - Payment Enhancement + +```go +type PaymentProcessorDecorator struct { + processor PaymentProcessor +} + +type LoggingDecorator struct { + PaymentProcessorDecorator + logger Logger +} + +func (d *LoggingDecorator) ProcessPayment(req *PaymentRequest) (*PaymentResponse, error) { + d.logger.Info("Processing payment", "amount", req.Amount, "method", req.Method) + resp, err := d.processor.ProcessPayment(req) + if err != nil { + d.logger.Error("Payment failed", "error", err) + } else { + d.logger.Info("Payment successful", "transaction_id", resp.TransactionID) + } + return resp, err +} +``` + +### **Database Schema Design** + +#### **Core Tables** + +```sql +-- Enrollments table +CREATE TABLE enrollments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + internship_id UUID NOT NULL REFERENCES internships(id), + status enrollment_status NOT NULL DEFAULT 'pending', + payment_id UUID REFERENCES payments(id), + enrolled_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + UNIQUE(user_id, internship_id) +); + +-- Payments table +CREATE TABLE payments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + status payment_status NOT NULL DEFAULT 'pending', + gateway VARCHAR(50) NOT NULL, + gateway_transaction_id VARCHAR(255), + payment_method VARCHAR(50), + metadata JSONB, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Payment attempts table +CREATE TABLE payment_attempts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + payment_id UUID NOT NULL REFERENCES payments(id), + gateway VARCHAR(50) NOT NULL, + status payment_status NOT NULL, + gateway_response JSONB, + error_message TEXT, + processed_at TIMESTAMP DEFAULT NOW() +); + +-- Discounts table +CREATE TABLE discounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + code VARCHAR(50) UNIQUE NOT NULL, + type discount_type NOT NULL, + value DECIMAL(10,2) NOT NULL, + currency VARCHAR(3), + usage_limit INTEGER, + used_count INTEGER DEFAULT 0, + valid_from TIMESTAMP, + valid_until TIMESTAMP, + applicable_to JSONB, -- courses, categories, users + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Refunds table +CREATE TABLE refunds ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + payment_id UUID NOT NULL REFERENCES payments(id), + amount DECIMAL(10,2) NOT NULL, + reason TEXT, + status refund_status NOT NULL DEFAULT 'pending', + gateway_refund_id VARCHAR(255), + processed_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW() +); + +-- Subscriptions table +CREATE TABLE subscriptions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + plan_id UUID NOT NULL REFERENCES subscription_plans(id), + status subscription_status NOT NULL DEFAULT 'active', + current_period_start TIMESTAMP NOT NULL, + current_period_end TIMESTAMP NOT NULL, + cancel_at_period_end BOOLEAN DEFAULT FALSE, + gateway VARCHAR(50) NOT NULL, + gateway_subscription_id VARCHAR(255), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Invoices table +CREATE TABLE invoices ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + subscription_id UUID REFERENCES subscriptions(id), + payment_id UUID REFERENCES payments(id), + invoice_number VARCHAR(50) UNIQUE NOT NULL, + amount DECIMAL(10,2) NOT NULL, + tax_amount DECIMAL(10,2) DEFAULT 0, + currency VARCHAR(3) NOT NULL, + status invoice_status NOT NULL DEFAULT 'draft', + due_date TIMESTAMP, + paid_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW() +); +``` + +#### **Enums** + +```sql +CREATE TYPE enrollment_status AS ENUM ('pending', 'enrolled', 'cancelled', 'expired', 'failed'); +CREATE TYPE payment_status AS ENUM ('pending', 'processing', 'succeeded', 'failed', 'cancelled', 'refunded'); +CREATE TYPE discount_type AS ENUM ('percentage', 'fixed_amount', 'free_shipping', 'bogo'); +CREATE TYPE refund_status AS ENUM ('pending', 'processing', 'succeeded', 'failed', 'cancelled'); +CREATE TYPE subscription_status AS ENUM ('active', 'cancelled', 'expired', 'past_due', 'unpaid'); +CREATE TYPE invoice_status AS ENUM ('draft', 'sent', 'paid', 'overdue', 'cancelled'); +``` + +--- + +## ๐Ÿ› ๏ธ Implementation Roadmap + +### **Phase 1: Foundation (Weeks 1-4)** + +#### **Week 1: Core Architecture Setup** + +- [ ] Set up project structure with Clean Architecture +- [ ] Implement basic payment gateway interface +- [ ] Create database schema and migrations +- [ ] Set up Docker development environment +- [ ] Configure CI/CD pipeline + +#### **Week 2: Payment Gateway Integration** + +- [ ] Implement Stripe gateway adapter +- [ ] Implement Razorpay gateway adapter +- [ ] Create payment gateway factory +- [ ] Add gateway health monitoring +- [ ] Write unit tests for gateway adapters + +#### **Week 3: Basic Enrollment Flow** + +- [ ] Create enrollment service +- [ ] Implement payment processing flow +- [ ] Add webhook handling for payment updates +- [ ] Create basic API endpoints +- [ ] Add authentication middleware + +#### **Week 4: Testing & Documentation** + +- [ ] Write comprehensive unit tests +- [ ] Add integration tests +- [ ] Create API documentation +- [ ] Set up monitoring and logging +- [ ] Performance testing + +### **Phase 2: Advanced Features (Weeks 5-8)** + +#### **Week 5: Discount System** + +- [ ] Implement coupon management +- [ ] Add discount calculation engine +- [ ] Create admin interface for discounts +- [ ] Add bulk discount features +- [ ] Test discount combinations + +#### **Week 6: Payment Methods Expansion** + +- [ ] Add UPI payment support +- [ ] Implement wallet integrations +- [ ] Add bank transfer support +- [ ] Create gift card system +- [ ] Add offline payment tracking + +#### **Week 7: Subscription Management** + +- [ ] Implement subscription models +- [ ] Add recurring billing +- [ ] Create subscription upgrade/downgrade +- [ ] Add dunning management +- [ ] Implement proration logic + +#### **Week 8: Refund & Dispute System** + +- [ ] Create refund processing system +- [ ] Add dispute management +- [ ] Implement automatic refund rules +- [ ] Add chargeback handling +- [ ] Create reconciliation system + +### **Phase 3: Enterprise Features (Weeks 9-12)** + +#### **Week 9: Invoicing & Billing** + +- [ ] Implement invoice generation +- [ ] Add tax calculation +- [ ] Create PDF invoice templates +- [ ] Add multi-currency support +- [ ] Implement billing cycles + +#### **Week 10: Reporting & Analytics** + +- [ ] Create payment analytics dashboard +- [ ] Add real-time monitoring +- [ ] Implement financial reporting +- [ ] Add fraud detection +- [ ] Create performance metrics + +#### **Week 11: Notification System** + +- [ ] Implement multi-channel notifications +- [ ] Add email template system +- [ ] Create SMS integration +- [ ] Add push notifications +- [ ] Implement notification preferences + +#### **Week 12: Security & Compliance** + +- [ ] Add PCI DSS compliance +- [ ] Implement data encryption +- [ ] Add audit logging +- [ ] Create security monitoring +- [ ] Add compliance reporting + +### **Phase 4: Optimization & Scaling (Weeks 13-16)** + +#### **Week 13: Performance Optimization** + +- [ ] Optimize database queries +- [ ] Add caching layers +- [ ] Implement connection pooling +- [ ] Add load balancing +- [ ] Performance profiling + +#### **Week 14: Advanced Gateway Features** + +- [ ] Add gateway failover +- [ ] Implement smart routing +- [ ] Add A/B testing for gateways +- [ ] Create gateway analytics +- [ ] Add cost optimization + +#### **Week 15: International Expansion** + +- [ ] Add multi-currency support +- [ ] Implement regional payment methods +- [ ] Add localization +- [ ] Create regional compliance +- [ ] Add currency conversion + +#### **Week 16: Launch Preparation** + +- [ ] Final testing and bug fixes +- [ ] Security audit +- [ ] Performance testing +- [ ] Documentation completion +- [ ] Production deployment + +--- + +## ๐Ÿ”’ Security Considerations + +### **Data Protection** + +- **Encryption**: All sensitive data encrypted at rest and in transit +- **PCI DSS Compliance**: Full compliance with payment card industry standards +- **Data Minimization**: Store only necessary payment information +- **Access Control**: Role-based access to payment data +- **Audit Logging**: Complete audit trail of all payment operations + +### **Fraud Prevention** + +- **Risk Scoring**: ML-based transaction risk assessment +- **Velocity Checks**: Monitor transaction patterns +- **Device Fingerprinting**: Track device characteristics +- **Geolocation**: Validate transaction locations +- **Blacklist Management**: Maintain fraud prevention lists + +### **API Security** + +- **Authentication**: JWT-based API authentication +- **Rate Limiting**: Prevent API abuse +- **Input Validation**: Comprehensive input sanitization +- **CORS**: Proper cross-origin resource sharing +- **Webhook Verification**: Verify webhook authenticity + +--- + +## ๐Ÿ“Š Success Metrics & KPIs + +### **Technical Metrics** + +- **Payment Success Rate**: > 95% +- **API Response Time**: < 500ms for 95th percentile +- **System Uptime**: 99.9% availability +- **Error Rate**: < 0.1% for critical operations +- **Database Performance**: < 100ms for 95th percentile queries + +### **Business Metrics** + +- **Conversion Rate**: % of users completing payment +- **Cart Abandonment**: % of users abandoning at payment +- **Revenue Growth**: Month-over-month growth +- **Customer Lifetime Value**: Average revenue per user +- **Refund Rate**: % of payments refunded + +### **User Experience Metrics** + +- **Payment Flow Completion**: Time to complete payment +- **User Satisfaction**: Payment experience rating +- **Support Tickets**: Payment-related support requests +- **Mobile Conversion**: Mobile vs desktop conversion rates +- **International Usage**: Global payment method adoption + +--- + +## ๐ŸŽฏ Future Roadmap + +### **Short-term (3-6 months)** + +- **Advanced Analytics**: ML-powered payment insights +- **Voice Payments**: Alexa/Google Assistant integration +- **Blockchain Payments**: Cryptocurrency support expansion +- **Marketplace Features**: Multi-vendor payment splitting +- **Mobile Payments**: Enhanced mobile wallet integration + +### **Medium-term (6-12 months)** + +- **AI-Powered Fraud Detection**: Advanced ML models +- **Instant Payouts**: Real-time merchant payouts +- **Embedded Finance**: White-label payment solutions +- **Open Banking**: Direct bank account payments +- **Carbon Offsetting**: Environmental impact tracking + +### **Long-term (12+ months)** + +- **Global Expansion**: Worldwide payment method support +- **Financial Services**: Lending and credit products +- **IoT Payments**: Connected device payments +- **Web3 Integration**: Decentralized payment protocols +- **Augmented Reality**: AR-powered payment experiences + +--- + +## ๐Ÿ“ž Support & Maintenance + +### **Development Team Structure** + +- **Lead Developer**: Overall architecture and technical decisions +- **Backend Developers**: API and service development +- **Frontend Developers**: Payment UI and user experience +- **DevOps Engineer**: Infrastructure and deployment +- **QA Engineer**: Testing and quality assurance +- **Security Engineer**: Security and compliance + +### **Ongoing Maintenance** + +- **Regular Updates**: Weekly security patches and updates +- **Performance Monitoring**: 24/7 system monitoring +- **Gateway Updates**: Stay current with gateway API changes +- **Compliance Reviews**: Quarterly compliance audits +- **User Feedback**: Continuous improvement based on feedback + +--- + +This PRD provides a comprehensive foundation for building a world-class internship enrollment workflow with payment integration. The system is designed to be scalable, maintainable, and feature-rich while following industry best practices and design patterns. diff --git a/docs/prds/ENROLLMENT_SYSTEM_ROADMAP.md b/docs/prds/ENROLLMENT_SYSTEM_ROADMAP.md new file mode 100644 index 0000000..eca37e4 --- /dev/null +++ b/docs/prds/ENROLLMENT_SYSTEM_ROADMAP.md @@ -0,0 +1,461 @@ +# ๐Ÿ—บ๏ธ LMS Enrollment System Implementation Roadmap + +## ๐Ÿ“‹ Executive Summary + +This roadmap provides a comprehensive implementation strategy for building a scalable LMS enrollment workflow with Razorpay payment integration, based on best practices from leading platforms like Udemy, Coursera, and LinkedIn Learning. + +## ๐Ÿ“š Documentation Structure + +### 1. **[LMS_ENROLLMENT_WORKFLOW_PRD.md](./LMS_ENROLLMENT_WORKFLOW_PRD.md)** + +- Complete Product Requirements Document +- Platform analysis (Udemy, Coursera, LinkedIn Learning) +- Workflow design and data flow architecture +- Technical architecture and design patterns +- Security, compliance, and analytics requirements + +### 2. **[ENROLLMENT_IMPLEMENTATION_GUIDE.md](./ENROLLMENT_IMPLEMENTATION_GUIDE.md)** + +- Step-by-step implementation instructions +- Database schema updates +- Service layer enhancements +- Razorpay integration details +- Frontend integration examples + +### 3. **[ENROLLMENT_API_EXAMPLES.md](./ENROLLMENT_API_EXAMPLES.md)** + +- Complete API request/response examples +- Error handling scenarios +- Test cases and validation +- Performance and security testing + +## ๐ŸŽฏ Key Implementation Insights + +### How Major Platforms Handle Enrollment + +#### **Udemy's Multi-Course Cart Model** + +``` +Course Discovery โ†’ Add to Cart โ†’ Apply Coupons โ†’ Checkout โ†’ Payment โ†’ Instant Access +``` + +- **Strengths**: Bulk purchasing, aggressive discounting +- **Implementation**: Shopping cart functionality with coupon stacking + +#### **Coursera's Subscription-First Approach** + +``` +Course Browse โ†’ Free Preview โ†’ Subscription/Purchase โ†’ Certificate Options +``` + +- **Strengths**: Recurring revenue, trial periods, financial aid +- **Implementation**: Freemium model with premium upgrades + +#### **LinkedIn Learning's Seamless Integration** + +``` +Discovery โ†’ Premium Check โ†’ Instant Access (if subscribed) +``` + +- **Strengths**: Zero-friction for subscribers, corporate accounts +- **Implementation**: Subscription-based with enterprise features + +### **Best Practices Identified** + +1. **Progressive Disclosure**: Show pricing and requirements gradually +2. **Payment Flexibility**: Multiple payment methods and options +3. **Error Recovery**: Graceful handling of payment failures +4. **Mobile Optimization**: Touch-friendly payment experience +5. **Instant Gratification**: Immediate course access upon payment +6. **Trust Building**: Clear refund policies and security indicators + +## ๐Ÿš€ Implementation Strategy + +### Phase 1: Foundation (Week 1-2) + +**Goal**: Establish core enrollment workflow + +#### **Database Schema** + +```sql +-- Enhanced enrollment table +ALTER TABLE enrollments ADD COLUMN enrollment_expires_at TIMESTAMP; +CREATE INDEX idx_enrollments_user_course ON enrollments(user_id, internship_id); +CREATE INDEX idx_enrollments_payment_lookup ON enrollments(payment_id); +``` + +#### **Core API Endpoints** + +- `POST /api/v1/enrollments/initialize` - Start enrollment process +- `GET /api/v1/enrollments/{id}/status` - Check enrollment status +- `POST /api/v1/enrollments/{id}/retry` - Retry failed payments + +#### **Razorpay Integration** + +- Enhanced order creation with proper metadata +- Webhook signature validation +- Payment status synchronization + +### Phase 2: Enhanced Features (Week 3-4) + +**Goal**: Add advanced functionality + +#### **Discount System** + +```go +type DiscountCalculator interface { + CalculateDiscount(originalAmount decimal.Decimal, coupons []string) (*DiscountResult, error) + ValidateCoupon(code string, courseID string, userID string) (*CouponValidation, error) +} +``` + +#### **Error Recovery** + +- Automatic payment retry logic +- User-friendly error messages +- Alternative payment method suggestions + +#### **Mobile Optimization** + +- Progressive Web App support +- Touch-optimized payment flow +- Offline enrollment queuing + +### Phase 3: Scale & Analytics (Week 5-6) + +**Goal**: Optimize for production + +#### **Performance Monitoring** + +```go +// Metrics to track +- enrollment_initiations_total +- enrollment_completions_total +- payment_processing_duration_seconds +- enrollment_conversion_rate +``` + +#### **Analytics Dashboard** + +- Real-time enrollment metrics +- Payment success rates by method +- Course popularity analytics +- Revenue tracking + +### Phase 4: Enterprise Features (Week 7-8) + +**Goal**: B2B capabilities + +#### **Bulk Enrollment** + +- Corporate account management +- Bulk payment processing +- Invoice generation +- Usage reporting + +## ๐Ÿ”„ API Workflow Summary + +### **Standard Enrollment Flow** + +```mermaid +sequenceDiagram + participant U as User + participant F as Frontend + participant A as API + participant R as Razorpay + participant W as Webhook + + U->>F: Click "Enroll" + F->>A: POST /enrollments/initialize + A->>R: Create Order + R-->>A: Order Details + A-->>F: Enrollment + Payment Session + F->>R: Open Checkout + U->>R: Complete Payment + R->>W: Payment Webhook + W->>A: Update Enrollment + A-->>F: Enrollment Complete + F-->>U: Course Access Granted +``` + +### **Key API Calls Order** + +1. **Course Information**: `GET /api/v1/internships/{courseId}` +2. **Initialize Enrollment**: `POST /api/v1/enrollments/initialize` +3. **Process Payment**: Frontend Razorpay integration +4. **Webhook Processing**: Backend payment confirmation +5. **Status Check**: `GET /api/v1/enrollments/{id}/status` +6. **Course Access**: `GET /api/v1/courses/{courseId}/access` + +## ๐Ÿ’ฐ Razorpay Integration Deep Dive + +### **Order Creation Strategy** + +```go +func (r *RazorpayProvider) CreateEnrollmentOrder(ctx context.Context, req *EnrollmentPaymentRequest) (*OrderResponse, error) { + orderData := map[string]interface{}{ + "amount": req.Amount * 100, // Convert to paise + "currency": req.Currency, + "receipt": req.IdempotencyKey, + "payment_capture": 1, // Auto-capture + "notes": map[string]string{ + "enrollment_id": req.EnrollmentID, + "course_id": req.CourseID, + "user_id": req.UserID, + }, + } + + return r.razorpayClient.Order.Create(orderData, nil) +} +``` + +### **Webhook Processing** + +```go +func (w *WebhookHandler) HandlePaymentSuccess(event *RazorpayEvent) error { + enrollmentID := event.Payload.Order.Notes["enrollment_id"] + + // Update payment status + payment.Status = "completed" + payment.GatewayPaymentID = event.Payload.Payment.ID + + // Complete enrollment + enrollment.Status = "enrolled" + enrollment.EnrolledAt = time.Now() + + // Grant course access + return w.grantCourseAccess(enrollmentID) +} +``` + +### **Frontend Integration** + +```javascript +// Complete enrollment flow +const enrollCourse = async (courseId) => { + // 1. Initialize enrollment + const enrollment = await initializeEnrollment(courseId); + + // 2. Process payment (if required) + if (enrollment.payment_required) { + await processRazorpayPayment(enrollment.payment_session); + } + + // 3. Verify completion + const status = await checkEnrollmentStatus(enrollment.enrollment_id); + + // 4. Redirect to course + if (status.enrollment_status === "enrolled") { + window.location.href = status.course_access.next_lesson_url; + } +}; +``` + +## ๐Ÿงช Testing Strategy + +### **Test Pyramid** + +#### **Unit Tests (70%)** + +- Service layer logic +- Payment calculations +- Validation functions +- Error handling + +#### **Integration Tests (20%)** + +- Database operations +- External API calls +- Webhook processing +- Payment flow end-to-end + +#### **E2E Tests (10%)** + +- Complete user journeys +- Cross-browser testing +- Mobile responsive testing +- Performance testing + +### **Critical Test Scenarios** + +```javascript +// Essential test cases +describe("Enrollment Workflow", () => { + test("Successful paid course enrollment"); + test("Free course immediate enrollment"); + test("Duplicate enrollment prevention"); + test("Payment failure recovery"); + test("Webhook processing reliability"); + test("Coupon application accuracy"); + test("Mobile payment experience"); +}); +``` + +## ๐Ÿ“Š Success Metrics + +### **Technical KPIs** + +- **API Response Time**: < 200ms for enrollment APIs +- **Payment Success Rate**: > 95% across all methods +- **System Uptime**: 99.9% availability +- **Error Rate**: < 1% for critical flows + +### **Business KPIs** + +- **Conversion Rate**: > 15% from course view to enrollment +- **Payment Abandonment**: < 10% at checkout +- **User Satisfaction**: > 4.5/5 rating +- **Revenue Growth**: Measurable increase in enrollments + +### **Monitoring Dashboard** + +```go +// Key metrics to track +type EnrollmentMetrics struct { + InitiationsToday int64 `json:"initiations_today"` + CompletionsToday int64 `json:"completions_today"` + ConversionRate float64 `json:"conversion_rate"` + AverageProcessingTime time.Duration `json:"avg_processing_time"` + PaymentSuccessRate float64 `json:"payment_success_rate"` + RevenueToday decimal.Decimal `json:"revenue_today"` +} +``` + +## ๐Ÿ”’ Security & Compliance + +### **Payment Security** + +- PCI DSS compliance through Razorpay +- Webhook signature validation +- Encrypted data transmission +- Audit logging for all transactions + +### **Data Protection** + +- GDPR compliance for EU users +- Data encryption at rest and transit +- User consent management +- Right to be forgotten implementation + +### **Fraud Prevention** + +```go +type FraudDetection struct { + RiskScoring RiskEngine + VelocityChecks VelocityLimiter + GeoValidation LocationValidator +} +``` + +## ๐Ÿš€ Deployment Checklist + +### **Pre-Production** + +- [ ] Database migrations tested +- [ ] Razorpay sandbox integration working +- [ ] Webhook endpoints configured +- [ ] SSL certificates installed +- [ ] Monitoring dashboards deployed + +### **Production Deployment** + +- [ ] Environment variables configured +- [ ] Razorpay production keys added +- [ ] Load balancer configured +- [ ] CDN setup for static assets +- [ ] Backup procedures tested + +### **Post-Deployment** + +- [ ] Smoke tests passed +- [ ] Monitoring alerts configured +- [ ] Error tracking enabled +- [ ] Performance baselines established +- [ ] Documentation updated + +## ๐ŸŽฏ Future Enhancements + +### **Phase 5: Advanced Features** + +- **AI-Powered Recommendations**: Course suggestions based on enrollment patterns +- **Dynamic Pricing**: Time-based and demand-based pricing +- **Social Learning**: Group enrollments and peer interactions +- **Gamification**: Achievement badges and learning streaks + +### **Phase 6: Global Expansion** + +- **Multi-Currency Support**: Automatic currency conversion +- **Regional Payment Methods**: Local payment preferences +- **Localization**: Multi-language support +- **Compliance**: Regional legal requirements + +## ๐Ÿ“ Implementation Priority + +### **High Priority (Must Have)** + +1. โœ… Basic enrollment workflow +2. โœ… Razorpay payment integration +3. โœ… Error handling and retry logic +4. โœ… Mobile-responsive design + +### **Medium Priority (Should Have)** + +1. ๐Ÿ“‹ Coupon and discount system +2. ๐Ÿ“‹ Analytics and reporting +3. ๐Ÿ“‹ Performance optimization +4. ๐Ÿ“‹ Security enhancements + +### **Low Priority (Nice to Have)** + +1. ๐Ÿ”ฎ Bulk enrollment features +2. ๐Ÿ”ฎ Advanced analytics +3. ๐Ÿ”ฎ AI recommendations +4. ๐Ÿ”ฎ Social features + +## ๐Ÿค Team Responsibilities + +### **Backend Team** + +- API development and optimization +- Database schema and migrations +- Payment gateway integration +- Security implementation + +### **Frontend Team** + +- User interface development +- Payment flow optimization +- Mobile responsiveness +- Error handling UX + +### **DevOps Team** + +- Infrastructure setup +- Monitoring and alerting +- Deployment automation +- Performance optimization + +### **QA Team** + +- Test case development +- End-to-end testing +- Performance testing +- Security testing + +## ๐Ÿ“ž Support & Maintenance + +### **Monitoring** + +- Real-time system health dashboards +- Payment processing alerts +- Error rate monitoring +- Performance metrics tracking + +### **Incident Response** + +- 24/7 payment processing monitoring +- Escalation procedures for failures +- Rollback procedures for deployments +- Communication plan for outages + +This roadmap provides a comprehensive strategy for implementing a world-class LMS enrollment system that can scale with your business growth and compete with industry leaders. diff --git a/docs/prds/HOSTINGER_INSPIRED_CHECKOUT_PRD.md b/docs/prds/HOSTINGER_INSPIRED_CHECKOUT_PRD.md new file mode 100644 index 0000000..dbe6bc4 --- /dev/null +++ b/docs/prds/HOSTINGER_INSPIRED_CHECKOUT_PRD.md @@ -0,0 +1,782 @@ +# ๐Ÿš€ **HOSTINGER-INSPIRED ROBUST CHECKOUT & ENROLLMENT FLOW PRD** + +## ๐Ÿ“‹ **Executive Summary** + +Based on detailed analysis of Hostinger's production checkout flow, this PRD outlines a robust, scalable, and error-resistant e-commerce checkout system for the Interns Go-Backend platform. The system incorporates proven patterns from Hostinger's architecture while adapting them for internship enrollment use cases. + +### ๐ŸŽฏ **Vision Statement** + +Create a bulletproof checkout experience that matches Hostinger's reliability and scalability, providing users with a seamless journey from internship discovery to successful enrollment, while maintaining enterprise-grade fault tolerance, security, and performance. + +--- + +## ๐Ÿ” **Hostinger Flow Analysis & Key Insights** + +### **Flow Breakdown Analysis** + +#### **1. Cart Creation Flow** + +``` +POST /api-proxy/api/cart +โ”œโ”€โ”€ Sale slug validation +โ”œโ”€โ”€ Product configuration +โ”œโ”€โ”€ Period selection (billing cycle) +โ”œโ”€โ”€ Analytics data collection +โ””โ”€โ”€ Cart token generation +``` + +**Key Insights:** + +- **Separate Cart Domain**: Dedicated cart.hostinger.com subdomain +- **Token-Based Cart**: UUID-based cart tokens for stateless operations +- **Analytics Integration**: Rich analytics data collection throughout flow +- **Sale Integration**: Dynamic sale/promotion system + +#### **2. Authentication & Authorization** + +``` +POST /api/v1/cart/{cart_token}/auth/check +โ”œโ”€โ”€ JWT token validation +โ”œโ”€โ”€ User session verification +โ”œโ”€โ”€ Cart ownership validation +โ””โ”€โ”€ Permission checks +``` + +**Key Insights:** + +- **Cart-Level Auth**: Authentication at cart level, not just user level +- **Session Management**: Robust session handling with device tracking +- **Correlation IDs**: Request tracing for debugging and monitoring + +#### **3. Pricing & Estimation** + +``` +POST /api/v1/cart/estimate +โ”œโ”€โ”€ Cart token validation +โ”œโ”€โ”€ Tax calculation (IGST 18%) +โ”œโ”€โ”€ Discount application +โ”œโ”€โ”€ Currency conversion +โ””โ”€โ”€ Final amount calculation +``` + +**Key Insights:** + +- **Real-Time Pricing**: Dynamic pricing with tax and discount calculations +- **Currency Handling**: Multi-currency support with conversion rates +- **Tax Integration**: Automated tax calculation based on location +- **Discount Transparency**: Clear breakdown of applied discounts + +#### **4. Order Creation & Payment** + +``` +POST /api/v1/order +โ”œโ”€โ”€ Cart validation +โ”œโ”€โ”€ Order token generation +โ”œโ”€โ”€ Payment token creation +โ”œโ”€โ”€ Razorpay integration +โ””โ”€โ”€ Payment session initiation +``` + +**Key Insights:** + +- **Dual Token System**: Separate order and payment tokens +- **Payment Gateway Integration**: Direct Razorpay integration +- **Session Management**: Payment session with token-based security + +--- + +## ๐Ÿ—๏ธ **Robust Architecture Design** + +### **System Architecture** + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Load Balancer โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ โ”‚ โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ” +โ”‚ Main API โ”‚ โ”‚ Cart โ”‚ โ”‚ Payment โ”‚ +โ”‚ Gateway โ”‚ โ”‚ Service โ”‚ โ”‚ Gateway โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ PostgreSQL DB โ”‚ + โ”‚ (Primary + Replica) โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Redis Cluster โ”‚ + โ”‚ (Session + Cache) โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Message Queue โ”‚ + โ”‚ (RabbitMQ/Kafka) โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### **Service Separation Strategy** + +#### **1. Cart Service (cart.interns.com)** + +- **Domain**: Dedicated subdomain for cart operations +- **Responsibilities**: Cart management, pricing, discounts +- **Data Store**: Redis for cart data, PostgreSQL for persistence +- **Scaling**: Horizontal scaling with load balancing + +#### **2. Payment Service (pay.interns.com)** + +- **Domain**: Dedicated subdomain for payment operations +- **Responsibilities**: Payment processing, gateway integration +- **Security**: PCI DSS compliance, token-based security +- **Isolation**: Complete isolation from main application + +#### **3. Main API Gateway** + +- **Responsibilities**: Authentication, routing, rate limiting +- **Security**: JWT validation, CORS, request sanitization +- **Monitoring**: Request tracing, error tracking + +--- + +## ๐Ÿ”ง **Robust Implementation Requirements** + +### **1. Cart Management System** + +#### **Cart Token Architecture** + +```go +type Cart struct { + Token string `json:"token"` // UUID-based cart token + UserID *string `json:"user_id"` // Optional, for guest carts + Status CartStatus `json:"status"` // draft, active, expired, converted + Items []CartItem `json:"items"` + Subtotal decimal.Decimal `json:"subtotal"` + Total decimal.Decimal `json:"total"` + Currency string `json:"currency"` + TaxRate decimal.Decimal `json:"tax_rate"` + TaxAmount decimal.Decimal `json:"tax_amount"` + DiscountCode *string `json:"discount_code"` + DiscountAmount decimal.Decimal `json:"discount_amount"` + ExpiresAt time.Time `json:"expires_at"` + DeviceID string `json:"device_id"` // For guest tracking + AnalyticsData []AnalyticsData `json:"analytics_data"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type CartItem struct { + InternshipID string `json:"internship_id"` + Title string `json:"title"` + Price decimal.Decimal `json:"price"` + Quantity int `json:"quantity"` + Period *Period `json:"period,omitempty"` // Billing period + Metadata map[string]any `json:"metadata,omitempty"` +} + +type Period struct { + Value int `json:"value"` // Number of months/years + Unit string `json:"unit"` // "month", "year" +} +``` + +#### **API Endpoints (Inspired by Hostinger)** + +``` +# Cart Creation & Management +POST /api-proxy/api/cart # Create cart with products +GET /api/v1/cart/{token} # Get cart details +PUT /api/v1/cart/{token}/plan/{plan_id} # Update cart plan +DELETE /api/v1/cart/{token}/item/{item_id} # Remove item + +# Authentication & Authorization +POST /api/v1/cart/{token}/auth/check # Check cart authentication +POST /api/v1/cart/{token}/auth/login # Login to cart + +# Pricing & Estimation +POST /api/v1/cart/estimate # Get cart pricing +GET /api/v1/cart/{token}/discounts # Get available discounts + +# Discount Management +POST /api/v1/cart/{token}/coupon # Apply coupon code +DELETE /api/v1/cart/{token}/coupon # Remove coupon + +# Order Creation +POST /api/v1/order # Create order from cart +GET /api/v1/order/{order_token} # Get order details +``` + +### **2. Robust Error Handling & Recovery** + +#### **Error Categories & Handling** + +```go +type ErrorResponse struct { + Status int `json:"status"` + Success bool `json:"success"` + Error *Error `json:"error,omitempty"` + Data any `json:"data,omitempty"` +} + +type Error struct { + Code int `json:"code"` + Message string `json:"message"` + ValidationMessages []string `json:"validation_messages,omitempty"` + CorrelationID string `json:"correlation_id"` + Retryable bool `json:"retryable"` + SuggestedAction string `json:"suggested_action,omitempty"` +} + +// Error Codes (Inspired by Hostinger) +const ( + ErrCartNotFound = 1001 + ErrCartExpired = 1002 + ErrInvalidCoupon = 2004 + ErrPaymentFailed = 3001 + ErrInsufficientBalance = 3002 + ErrGatewayTimeout = 4001 + ErrRateLimitExceeded = 429 +) +``` + +#### **Retry Mechanisms** + +```go +type RetryConfig struct { + MaxAttempts int `json:"max_attempts"` + InitialDelay time.Duration `json:"initial_delay"` + MaxDelay time.Duration `json:"max_delay"` + BackoffFactor float64 `json:"backoff_factor"` + RetryableErrors []int `json:"retryable_errors"` +} + +// Retry Strategy +func (s *CartService) CreateCartWithRetry(ctx context.Context, req *CreateCartRequest) (*CartResponse, error) { + var lastErr error + for attempt := 1; attempt <= s.config.MaxAttempts; attempt++ { + cart, err := s.createCart(ctx, req) + if err == nil { + return cart, nil + } + + if !s.isRetryableError(err) { + return nil, err + } + + lastErr = err + delay := s.calculateBackoffDelay(attempt) + time.Sleep(delay) + } + + return nil, fmt.Errorf("max retry attempts exceeded: %w", lastErr) +} +``` + +### **3. Scalable Data Architecture** + +#### **Database Design** + +```sql +-- Cart Table (Optimized for performance) +CREATE TABLE carts ( + token VARCHAR(36) PRIMARY KEY, + user_id UUID REFERENCES users(id), + status cart_status DEFAULT 'draft', + items JSONB NOT NULL DEFAULT '[]', + subtotal DECIMAL(10,2) NOT NULL DEFAULT 0, + total DECIMAL(10,2) NOT NULL DEFAULT 0, + currency VARCHAR(3) NOT NULL DEFAULT 'INR', + tax_rate DECIMAL(5,2) NOT NULL DEFAULT 0, + tax_amount DECIMAL(10,2) NOT NULL DEFAULT 0, + discount_code VARCHAR(50), + discount_amount DECIMAL(10,2) NOT NULL DEFAULT 0, + expires_at TIMESTAMP NOT NULL, + device_id VARCHAR(255), + analytics_data JSONB DEFAULT '[]', + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + + -- Indexes for performance + INDEX idx_carts_user_id (user_id), + INDEX idx_carts_status (status), + INDEX idx_carts_expires_at (expires_at), + INDEX idx_carts_device_id (device_id) +); + +-- Order Table (Separate from cart) +CREATE TABLE orders ( + order_token VARCHAR(36) PRIMARY KEY, + cart_token VARCHAR(36) NOT NULL REFERENCES carts(token), + user_id UUID NOT NULL REFERENCES users(id), + status order_status DEFAULT 'pending', + currency VARCHAR(3) NOT NULL DEFAULT 'INR', + subtotal DECIMAL(10,2) NOT NULL, + total DECIMAL(10,2) NOT NULL, + tax_amount DECIMAL(10,2) NOT NULL DEFAULT 0, + discount_amount DECIMAL(10,2) NOT NULL DEFAULT 0, + payment_token VARCHAR(36), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + + INDEX idx_orders_cart_token (cart_token), + INDEX idx_orders_user_id (user_id), + INDEX idx_orders_status (status), + INDEX idx_orders_payment_token (payment_token) +); + +-- Payment Tokens Table +CREATE TABLE payment_tokens ( + token VARCHAR(36) PRIMARY KEY, + order_token VARCHAR(36) NOT NULL REFERENCES orders(order_token), + gateway VARCHAR(50) NOT NULL, + gateway_order_id VARCHAR(255), + amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL, + status payment_status DEFAULT 'pending', + expires_at TIMESTAMP NOT NULL, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + + INDEX idx_payment_tokens_order_token (order_token), + INDEX idx_payment_tokens_status (status), + INDEX idx_payment_tokens_expires_at (expires_at) +); +``` + +#### **Caching Strategy** + +```go +type CacheConfig struct { + CartTTL time.Duration `json:"cart_ttl"` // 24 hours + PricingTTL time.Duration `json:"pricing_ttl"` // 5 minutes + UserSessionTTL time.Duration `json:"user_session_ttl"` // 30 minutes + DiscountTTL time.Duration `json:"discount_ttl"` // 1 hour +} + +// Redis Cache Keys +const ( + CartKeyPrefix = "cart:" + PricingKeyPrefix = "pricing:" + SessionKeyPrefix = "session:" + DiscountKeyPrefix = "discount:" +) + +func (s *CartService) GetCartWithCache(ctx context.Context, token string) (*Cart, error) { + // Try cache first + cacheKey := CartKeyPrefix + token + if cached, err := s.cache.Get(ctx, cacheKey); err == nil { + var cart Cart + if err := json.Unmarshal([]byte(cached), &cart); err == nil { + return &cart, nil + } + } + + // Fallback to database + cart, err := s.repo.GetByToken(ctx, token) + if err != nil { + return nil, err + } + + // Cache the result + if cartData, err := json.Marshal(cart); err == nil { + s.cache.Set(ctx, cacheKey, string(cartData), s.config.CartTTL) + } + + return cart, nil +} +``` + +### **4. Payment Integration (Razorpay-Inspired)** + +#### **Payment Flow Architecture** + +```go +type PaymentService struct { + razorpayClient *razorpay.Client + stripeClient *stripe.Client + cache cache.Cache + logger *logger.Logger +} + +type PaymentRequest struct { + OrderToken string `json:"order_token"` + Amount decimal.Decimal `json:"amount"` + Currency string `json:"currency"` + PaymentMethod string `json:"payment_method"` + UPIID *string `json:"upi_id,omitempty"` + CardToken *string `json:"card_token,omitempty"` + ReturnURL string `json:"return_url"` + CancelURL string `json:"cancel_url"` +} + +type PaymentResponse struct { + PaymentToken string `json:"payment_token"` + GatewayOrderID string `json:"gateway_order_id"` + CheckoutURL string `json:"checkout_url"` + Status string `json:"status"` + ExpiresAt time.Time `json:"expires_at"` +} + +func (s *PaymentService) CreatePaymentSession(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) { + // Create payment token + paymentToken := uuid.New().String() + + // Create gateway order + gatewayOrder, err := s.createGatewayOrder(ctx, req) + if err != nil { + return nil, fmt.Errorf("gateway order creation failed: %w", err) + } + + // Store payment token + paymentTokenData := &PaymentToken{ + Token: paymentToken, + OrderToken: req.OrderToken, + Gateway: "razorpay", + GatewayOrderID: gatewayOrder.ID, + Amount: req.Amount, + Currency: req.Currency, + Status: "pending", + ExpiresAt: time.Now().Add(30 * time.Minute), + } + + if err := s.repo.CreatePaymentToken(ctx, paymentTokenData); err != nil { + return nil, err + } + + return &PaymentResponse{ + PaymentToken: paymentToken, + GatewayOrderID: gatewayOrder.ID, + CheckoutURL: gatewayOrder.CheckoutURL, + Status: "pending", + ExpiresAt: paymentTokenData.ExpiresAt, + }, nil +} +``` + +### **5. Webhook & Event System** + +#### **Robust Webhook Handling** + +```go +type WebhookHandler struct { + signatureVerifier SignatureVerifier + eventProcessor EventProcessor + retryQueue queue.Queue + logger *logger.Logger +} + +func (h *WebhookHandler) HandleRazorpayWebhook(ctx context.Context, payload []byte, headers map[string]string) error { + // Verify signature + if err := h.signatureVerifier.Verify(payload, headers["X-Razorpay-Signature"]); err != nil { + h.logger.Errorw("webhook signature verification failed", "error", err) + return http.StatusBadRequest + } + + // Parse event + var event RazorpayEvent + if err := json.Unmarshal(payload, &event); err != nil { + h.logger.Errorw("webhook payload parsing failed", "error", err) + return http.StatusBadRequest + } + + // Process event with retry + if err := h.processEventWithRetry(ctx, &event); err != nil { + h.logger.Errorw("webhook event processing failed", "event_id", event.ID, "error", err) + + // Queue for retry + h.retryQueue.Publish(ctx, &RetryMessage{ + EventID: event.ID, + Payload: payload, + Attempts: 1, + NextRetry: time.Now().Add(5 * time.Minute), + }) + + return http.StatusInternalServerError + } + + return http.StatusOK +} + +func (h *WebhookHandler) processEventWithRetry(ctx context.Context, event *RazorpayEvent) error { + // Check if already processed + if h.isEventProcessed(ctx, event.ID) { + return nil // Idempotent + } + + // Process based on event type + switch event.Event { + case "payment.captured": + return h.handlePaymentSuccess(ctx, event) + case "payment.failed": + return h.handlePaymentFailure(ctx, event) + case "order.paid": + return h.handleOrderPaid(ctx, event) + default: + h.logger.Warnw("unknown webhook event type", "event_type", event.Event) + return nil + } +} +``` + +### **6. Monitoring & Observability** + +#### **Comprehensive Monitoring** + +```go +type Metrics struct { + CartCreated prometheus.Counter + CartConverted prometheus.Counter + PaymentSuccess prometheus.Counter + PaymentFailed prometheus.Counter + WebhookProcessed prometheus.Counter + WebhookFailed prometheus.Counter + ResponseTime prometheus.Histogram + ErrorRate prometheus.Counter +} + +type Tracing struct { + tracer trace.Tracer +} + +func (s *CartService) CreateCart(ctx context.Context, req *CreateCartRequest) (*CartResponse, error) { + start := time.Now() + defer func() { + s.metrics.ResponseTime.Observe(time.Since(start).Seconds()) + }() + + // Create span for tracing + ctx, span := s.tracing.tracer.Start(ctx, "cart.create") + defer span.End() + + // Add correlation ID + correlationID := uuid.New().String() + ctx = context.WithValue(ctx, "correlation_id", correlationID) + + // Process request + cart, err := s.processCreateCart(ctx, req) + if err != nil { + s.metrics.ErrorRate.Inc() + s.logger.Errorw("cart creation failed", + "correlation_id", correlationID, + "error", err, + "user_id", req.UserID) + return nil, err + } + + s.metrics.CartCreated.Inc() + s.logger.Infow("cart created successfully", + "correlation_id", correlationID, + "cart_token", cart.Token, + "user_id", req.UserID) + + return cart, nil +} +``` + +--- + +## ๐Ÿš€ **Implementation Roadmap (1 Day)** + +### **Phase 1: Foundation (Hours 1-4)** + +#### **Hour 1: Database Schema & Migrations** + +- [ ] Create cart, order, payment_tokens tables +- [ ] Implement database indexes for performance +- [ ] Set up database connection pooling +- [ ] Create database migration scripts + +#### **Hour 2: Domain Models & Repositories** + +- [ ] Implement Cart, Order, PaymentToken domain models +- [ ] Create repository interfaces and implementations +- [ ] Add validation logic for domain models +- [ ] Implement caching layer with Redis + +#### **Hour 3: Service Layer Foundation** + +- [ ] Create CartService with basic CRUD operations +- [ ] Implement OrderService with order lifecycle +- [ ] Add PaymentService with gateway integration +- [ ] Set up error handling and retry mechanisms + +#### **Hour 4: API Layer Scaffolding** + +- [ ] Create API routes for cart operations +- [ ] Implement authentication middleware +- [ ] Add request validation and sanitization +- [ ] Set up CORS and security headers + +### **Phase 2: Core Functionality (Hours 5-8)** + +#### **Hour 5: Cart Management** + +- [ ] Implement cart creation with token generation +- [ ] Add cart item management (add, remove, update) +- [ ] Implement cart pricing calculation +- [ ] Add cart expiration and cleanup + +#### **Hour 6: Pricing & Discounts** + +- [ ] Implement real-time pricing calculation +- [ ] Add tax calculation based on location +- [ ] Implement coupon code validation and application +- [ ] Add discount transparency and breakdown + +#### **Hour 7: Order Creation** + +- [ ] Implement order creation from cart +- [ ] Add order token generation +- [ ] Implement order status management +- [ ] Add order validation and security checks + +#### **Hour 8: Payment Integration** + +- [ ] Complete Razorpay provider implementation +- [ ] Add payment session creation +- [ ] Implement payment token management +- [ ] Add payment status tracking + +### **Phase 3: Robustness & Reliability (Hours 9-12)** + +#### **Hour 9: Error Handling & Recovery** + +- [ ] Implement comprehensive error handling +- [ ] Add retry mechanisms with exponential backoff +- [ ] Implement circuit breaker pattern +- [ ] Add graceful degradation + +#### **Hour 10: Webhook System** + +- [ ] Implement webhook signature verification +- [ ] Add webhook event processing +- [ ] Implement idempotency for webhooks +- [ ] Add webhook retry queue + +#### **Hour 11: Event-Driven Architecture** + +- [ ] Set up message queue (RabbitMQ/Kafka) +- [ ] Implement event publishing +- [ ] Add event consumers for enrollment +- [ ] Implement dead letter queue + +#### **Hour 12: Monitoring & Observability** + +- [ ] Add comprehensive logging +- [ ] Implement metrics collection +- [ ] Add distributed tracing +- [ ] Set up health checks + +### **Phase 4: Production Readiness (Hours 13-16)** + +#### **Hour 13: Security Hardening** + +- [ ] Implement rate limiting +- [ ] Add input validation and sanitization +- [ ] Implement CORS policies +- [ ] Add security headers + +#### **Hour 14: Performance Optimization** + +- [ ] Optimize database queries +- [ ] Implement connection pooling +- [ ] Add caching strategies +- [ ] Optimize API response times + +#### **Hour 15: Testing & Validation** + +- [ ] Write unit tests for core services +- [ ] Implement integration tests +- [ ] Add load testing scripts +- [ ] Test error scenarios + +#### **Hour 16: Documentation & Deployment** + +- [ ] Create API documentation +- [ ] Write deployment guides +- [ ] Add monitoring dashboards +- [ ] Prepare rollback procedures + +--- + +## ๐ŸŽฏ **Success Criteria** + +### **Functional Success** + +- [ ] Cart creation and management works flawlessly +- [ ] Pricing calculation is accurate and real-time +- [ ] Payment processing succeeds with multiple gateways +- [ ] Webhook processing is reliable and idempotent +- [ ] Order-to-enrollment flow is automated + +### **Performance Success** + +- [ ] API response times < 200ms (P95) +- [ ] System handles 10,000+ concurrent users +- [ ] Payment success rate > 98% +- [ ] Webhook processing < 1s average + +### **Reliability Success** + +- [ ] 99.9% system uptime +- [ ] Zero data loss in payment processing +- [ ] Graceful handling of gateway failures +- [ ] Automatic recovery from transient errors + +### **Security Success** + +- [ ] PCI DSS compliance for payment data +- [ ] Secure webhook signature verification +- [ ] Protection against common attacks +- [ ] Audit trail for all transactions + +--- + +## ๐Ÿšจ **Risk Mitigation Strategies** + +### **Technical Risks** + +| Risk | Impact | Mitigation Strategy | +| ------------------------ | ------ | ----------------------------------------------------- | +| Payment Gateway Downtime | High | Multiple gateway fallback, circuit breaker | +| Database Performance | Medium | Query optimization, read replicas, caching | +| Webhook Failures | High | Retry queue, dead letter queue, manual reconciliation | +| Memory Leaks | Medium | Connection pooling, resource cleanup, monitoring | + +### **Business Risks** + +| Risk | Impact | Mitigation Strategy | +| ------------------ | -------- | ------------------------------------------------- | +| Cart Abandonment | High | Cart persistence, email reminders, A/B testing | +| Payment Failures | High | Multiple payment methods, clear error messages | +| Data Loss | Critical | Database backups, transaction logging, monitoring | +| Scalability Issues | Medium | Horizontal scaling, load balancing, auto-scaling | + +--- + +## ๐Ÿ“Š **Monitoring & Alerting** + +### **Key Metrics to Track** + +- Cart creation rate and conversion +- Payment success/failure rates +- API response times and error rates +- Webhook processing latency +- Database performance metrics +- Cache hit/miss ratios + +### **Alerting Rules** + +- Payment success rate < 95% +- API response time > 500ms +- Webhook processing failure rate > 5% +- Database connection pool exhaustion +- Cache miss rate > 20% + +--- + +This PRD provides a comprehensive blueprint for implementing a robust, scalable, and error-resistant checkout system inspired by Hostinger's proven architecture while adapting it for internship enrollment use cases. diff --git a/docs/prds/IMPLEMENTATION_GUIDE.md b/docs/prds/IMPLEMENTATION_GUIDE.md new file mode 100644 index 0000000..9c8029b --- /dev/null +++ b/docs/prds/IMPLEMENTATION_GUIDE.md @@ -0,0 +1,830 @@ +# ๐Ÿ”ง Low-Level Implementation Guide + +This document provides detailed implementation instructions for all major components of the internship platform. + +--- + +## ๐Ÿ“‹ Day 1: Enrollment System Implementation + +### **1. Create Enrollment Service** + +**File: `internal/service/enrollment.go`** + +```go +package service + +import ( + "context" + "fmt" + "time" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/domain/enrollment" + "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/types" +) + +type EnrollmentService struct { + logger *logger.Logger + entClient *ent.Client + paymentSvc PaymentService + notifySvc NotificationService + enrollRepo enrollment.Repository +} + +func NewEnrollmentService( + logger *logger.Logger, + entClient *ent.Client, + paymentSvc PaymentService, + notifySvc NotificationService, + enrollRepo enrollment.Repository, +) *EnrollmentService { + return &EnrollmentService{ + logger: logger, + entClient: entClient, + paymentSvc: paymentSvc, + notifySvc: notifySvc, + enrollRepo: enrollRepo, + } +} + +func (s *EnrollmentService) ApplyForInternship( + ctx context.Context, + req *dto.ApplyInternshipRequest, +) (*dto.EnrollmentResponse, error) { + // 1. Validate input + if err := s.validateApplicationRequest(ctx, req); err != nil { + return nil, err + } + + // 2. Check if user already applied + existingEnrollment, err := s.enrollRepo.GetByUserAndInternship( + ctx, req.UserID, req.InternshipID, + ) + if err != nil && !ent.IsNotFound(err) { + return nil, fmt.Errorf("failed to check existing enrollment: %w", err) + } + if existingEnrollment != nil { + return nil, errors.NewValidationError("already_applied", "You have already applied for this internship") + } + + // 3. Get internship details for payment + internship, err := s.entClient.Internship.Get(ctx, req.InternshipID) + if err != nil { + return nil, fmt.Errorf("failed to get internship: %w", err) + } + + // 4. Create payment request + paymentReq := &dto.PaymentRequest{ + Amount: internship.Price, + Currency: internship.Currency, + DestinationType: types.PaymentDestinationTypeEnrollment, + UserID: req.UserID, + Metadata: map[string]string{ + "internship_id": req.InternshipID, + "user_id": req.UserID, + }, + } + + paymentResp, err := s.paymentSvc.CreatePaymentRequest(ctx, paymentReq) + if err != nil { + return nil, fmt.Errorf("failed to create payment: %w", err) + } + + // 5. Create enrollment record + enrollment, err := s.entClient.Enrollment. + Create(). + SetUserID(req.UserID). + SetInternshipID(req.InternshipID). + SetEnrollmentStatus(types.EnrollmentStatusPending). + SetPaymentID(paymentResp.PaymentID). + Save(ctx) + if err != nil { + return nil, fmt.Errorf("failed to create enrollment: %w", err) + } + + // 6. Send notification + go func() { + s.notifySvc.SendInAppNotification(context.Background(), req.UserID, &dto.Notification{ + Title: "Application Submitted", + Content: fmt.Sprintf("Your application for %s has been submitted. Please complete payment.", internship.Title), + Type: types.NotificationTypeApplicationStatus, + }) + }() + + return &dto.EnrollmentResponse{ + EnrollmentID: enrollment.ID, + PaymentURL: paymentResp.RedirectURL, + PaymentID: paymentResp.PaymentID, + Status: string(enrollment.EnrollmentStatus), + InternshipID: enrollment.InternshipID, + CreatedAt: enrollment.CreatedAt, + }, nil +} + +func (s *EnrollmentService) ProcessPaymentWebhook( + ctx context.Context, + paymentID string, + status types.PaymentStatus, +) error { + // 1. Get enrollment by payment ID + enrollment, err := s.enrollRepo.GetByPaymentID(ctx, paymentID) + if err != nil { + return fmt.Errorf("failed to get enrollment by payment ID: %w", err) + } + + // 2. Update enrollment status based on payment status + var newStatus types.EnrollmentStatus + switch status { + case types.PaymentStatusSucceeded: + newStatus = types.EnrollmentStatusEnrolled + case types.PaymentStatusFailed: + newStatus = types.EnrollmentStatusFailed + case types.PaymentStatusCancelled: + newStatus = types.EnrollmentStatusCancelled + default: + return fmt.Errorf("unsupported payment status: %s", status) + } + + // 3. Update enrollment + _, err = s.entClient.Enrollment. + UpdateOneID(enrollment.ID). + SetEnrollmentStatus(newStatus). + SetNillableEnrolledAt(func() *time.Time { + if newStatus == types.EnrollmentStatusEnrolled { + now := time.Now() + return &now + } + return nil + }()). + Save(ctx) + if err != nil { + return fmt.Errorf("failed to update enrollment status: %w", err) + } + + // 4. Send notification + go func() { + var title, content string + switch newStatus { + case types.EnrollmentStatusEnrolled: + title = "Payment Successful - Welcome!" + content = "Your payment has been processed. Welcome to the internship!" + case types.EnrollmentStatusFailed: + title = "Payment Failed" + content = "Your payment could not be processed. Please try again." + case types.EnrollmentStatusCancelled: + title = "Payment Cancelled" + content = "Your payment was cancelled. You can retry anytime." + } + + s.notifySvc.SendInAppNotification(context.Background(), enrollment.UserID, &dto.Notification{ + Title: title, + Content: content, + Type: types.NotificationTypePaymentConfirmation, + }) + }() + + return nil +} + +func (s *EnrollmentService) GetUserApplications( + ctx context.Context, + userID string, + pagination *types.Pagination, +) ([]*dto.UserApplicationResponse, error) { + enrollments, err := s.enrollRepo.GetUserEnrollments(ctx, userID, pagination) + if err != nil { + return nil, fmt.Errorf("failed to get user enrollments: %w", err) + } + + var responses []*dto.UserApplicationResponse + for _, e := range enrollments { + responses = append(responses, &dto.UserApplicationResponse{ + EnrollmentID: e.ID, + InternshipID: e.InternshipID, + InternshipTitle: e.Edges.Internship.Title, + Status: string(e.EnrollmentStatus), + AppliedAt: e.CreatedAt, + EnrolledAt: e.EnrolledAt, + PaymentStatus: s.getPaymentStatus(e.PaymentID), + }) + } + + return responses, nil +} + +func (s *EnrollmentService) GetPeerApplications( + ctx context.Context, + internshipID string, + currentUserID string, +) (*dto.PeerApplicationsResponse, error) { + enrollments, err := s.enrollRepo.GetInternshipEnrollments(ctx, internshipID) + if err != nil { + return nil, fmt.Errorf("failed to get internship enrollments: %w", err) + } + + response := &dto.PeerApplicationsResponse{ + InternshipID: internshipID, + Stats: dto.ApplicationStats{ + Total: len(enrollments), + Pending: 0, + Enrolled: 0, + Failed: 0, + }, + Peers: make([]*dto.PeerApplication, 0), + } + + for _, e := range enrollments { + // Update stats + switch e.EnrollmentStatus { + case types.EnrollmentStatusPending: + response.Stats.Pending++ + case types.EnrollmentStatusEnrolled: + response.Stats.Enrolled++ + case types.EnrollmentStatusFailed, types.EnrollmentStatusCancelled: + response.Stats.Failed++ + } + + // Add peer info (anonymized) + response.Peers = append(response.Peers, &dto.PeerApplication{ + UserID: func() string { + if e.UserID == currentUserID { + return e.UserID // Show full ID for current user + } + return "user_" + e.UserID[len(e.UserID)-4:] // Anonymize others + }(), + Status: string(e.EnrollmentStatus), + AppliedAt: e.CreatedAt, + IsYou: e.UserID == currentUserID, + }) + } + + return response, nil +} + +func (s *EnrollmentService) validateApplicationRequest( + ctx context.Context, + req *dto.ApplyInternshipRequest, +) error { + // Check if internship exists and is active + internship, err := s.entClient.Internship.Get(ctx, req.InternshipID) + if err != nil { + if ent.IsNotFound(err) { + return errors.NewValidationError("internship_not_found", "Internship not found") + } + return err + } + + // Add more validation logic here + _ = internship // Use internship for validation + + return nil +} + +func (s *EnrollmentService) getPaymentStatus(paymentID *string) string { + if paymentID == nil { + return "not_initiated" + } + // Implement payment status lookup + return "pending" +} +``` + +### **2. Create Enrollment Repository** + +**File: `internal/repository/ent/enrollment.go`** + +```go +package ent + +import ( + "context" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/ent/enrollment" + "github.com/omkar273/codegeeky/internal/domain/enrollment" + "github.com/omkar273/codegeeky/internal/types" +) + +type enrollmentRepository struct { + client *ent.Client +} + +func NewEnrollmentRepository(client *ent.Client) enrollment.Repository { + return &enrollmentRepository{client: client} +} + +func (r *enrollmentRepository) Create(ctx context.Context, req *enrollment.CreateRequest) (*ent.Enrollment, error) { + return r.client.Enrollment. + Create(). + SetUserID(req.UserID). + SetInternshipID(req.InternshipID). + SetEnrollmentStatus(req.Status). + SetNillablePaymentID(req.PaymentID). + Save(ctx) +} + +func (r *enrollmentRepository) GetByUserAndInternship( + ctx context.Context, + userID, internshipID string, +) (*ent.Enrollment, error) { + return r.client.Enrollment. + Query(). + Where( + enrollment.UserID(userID), + enrollment.InternshipID(internshipID), + ). + First(ctx) +} + +func (r *enrollmentRepository) GetByPaymentID( + ctx context.Context, + paymentID string, +) (*ent.Enrollment, error) { + return r.client.Enrollment. + Query(). + Where(enrollment.PaymentID(paymentID)). + First(ctx) +} + +func (r *enrollmentRepository) GetUserEnrollments( + ctx context.Context, + userID string, + pagination *types.Pagination, +) ([]*ent.Enrollment, error) { + query := r.client.Enrollment. + Query(). + Where(enrollment.UserID(userID)). + WithInternship() + + if pagination != nil { + query = query. + Limit(pagination.Limit). + Offset(pagination.Offset) + } + + return query.All(ctx) +} + +func (r *enrollmentRepository) GetInternshipEnrollments( + ctx context.Context, + internshipID string, +) ([]*ent.Enrollment, error) { + return r.client.Enrollment. + Query(). + Where(enrollment.InternshipID(internshipID)). + WithUser(). + All(ctx) +} + +func (r *enrollmentRepository) UpdateStatus( + ctx context.Context, + enrollmentID string, + status types.EnrollmentStatus, +) (*ent.Enrollment, error) { + return r.client.Enrollment. + UpdateOneID(enrollmentID). + SetEnrollmentStatus(status). + Save(ctx) +} +``` + +### **3. Create Enrollment API Handler** + +**File: `internal/api/v1/enrollment.go`** + +```go +package v1 + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/service" + "github.com/omkar273/codegeeky/internal/types" +) + +type EnrollmentHandler struct { + logger *logger.Logger + enrollmentSvc *service.EnrollmentService +} + +func NewEnrollmentHandler( + logger *logger.Logger, + enrollmentSvc *service.EnrollmentService, +) *EnrollmentHandler { + return &EnrollmentHandler{ + logger: logger, + enrollmentSvc: enrollmentSvc, + } +} + +// ApplyForInternship godoc +// @Summary Apply for an internship +// @Description Submit an application for an internship with payment +// @Tags enrollments +// @Accept json +// @Produce json +// @Param id path string true "Internship ID" +// @Param request body dto.ApplyInternshipRequest true "Application request" +// @Success 201 {object} dto.EnrollmentResponse +// @Failure 400 {object} errors.ErrorResponse +// @Failure 500 {object} errors.ErrorResponse +// @Router /v1/internships/{id}/apply [post] +func (h *EnrollmentHandler) ApplyForInternship(c *gin.Context) { + internshipID := c.Param("id") + userID := c.GetString("user_id") // From auth middleware + + var req dto.ApplyInternshipRequest + if err := c.ShouldBindJSON(&req); err != nil { + h.logger.Errorw("failed to bind request", "error", err) + c.JSON(http.StatusBadRequest, errors.NewValidationError("invalid_request", err.Error())) + return + } + + req.InternshipID = internshipID + req.UserID = userID + + resp, err := h.enrollmentSvc.ApplyForInternship(c.Request.Context(), &req) + if err != nil { + h.logger.Errorw("failed to apply for internship", "error", err, "user_id", userID, "internship_id", internshipID) + + if valErr, ok := err.(*errors.ValidationError); ok { + c.JSON(http.StatusBadRequest, valErr) + return + } + + c.JSON(http.StatusInternalServerError, errors.NewInternalError("application_failed", "Failed to submit application")) + return + } + + c.JSON(http.StatusCreated, resp) +} + +// GetUserApplications godoc +// @Summary Get user's applications +// @Description Get all applications submitted by the current user +// @Tags enrollments +// @Produce json +// @Param page query int false "Page number" +// @Param limit query int false "Page size" +// @Success 200 {object} dto.UserApplicationsResponse +// @Failure 500 {object} errors.ErrorResponse +// @Router /v1/user/applications [get] +func (h *EnrollmentHandler) GetUserApplications(c *gin.Context) { + userID := c.GetString("user_id") + + pagination := &types.Pagination{ + Page: c.GetInt("page"), + Limit: c.GetInt("limit"), + Offset: (c.GetInt("page") - 1) * c.GetInt("limit"), + } + + applications, err := h.enrollmentSvc.GetUserApplications(c.Request.Context(), userID, pagination) + if err != nil { + h.logger.Errorw("failed to get user applications", "error", err, "user_id", userID) + c.JSON(http.StatusInternalServerError, errors.NewInternalError("fetch_failed", "Failed to fetch applications")) + return + } + + c.JSON(http.StatusOK, dto.UserApplicationsResponse{ + Applications: applications, + Pagination: pagination, + }) +} + +// GetPeerApplications godoc +// @Summary Get peer applications for an internship +// @Description Get anonymized application status of other students for the same internship +// @Tags enrollments +// @Produce json +// @Param id path string true "Internship ID" +// @Success 200 {object} dto.PeerApplicationsResponse +// @Failure 500 {object} errors.ErrorResponse +// @Router /v1/internships/{id}/peers [get] +func (h *EnrollmentHandler) GetPeerApplications(c *gin.Context) { + internshipID := c.Param("id") + userID := c.GetString("user_id") + + peers, err := h.enrollmentSvc.GetPeerApplications(c.Request.Context(), internshipID, userID) + if err != nil { + h.logger.Errorw("failed to get peer applications", "error", err, "internship_id", internshipID) + c.JSON(http.StatusInternalServerError, errors.NewInternalError("fetch_failed", "Failed to fetch peer applications")) + return + } + + c.JSON(http.StatusOK, peers) +} + +// UpdateEnrollmentStatus godoc +// @Summary Update enrollment status (Admin only) +// @Description Update the status of an enrollment (interview, accept, reject) +// @Tags enrollments +// @Accept json +// @Produce json +// @Param id path string true "Enrollment ID" +// @Param request body dto.UpdateEnrollmentStatusRequest true "Status update request" +// @Success 200 {object} dto.EnrollmentResponse +// @Failure 400 {object} errors.ErrorResponse +// @Failure 403 {object} errors.ErrorResponse +// @Failure 500 {object} errors.ErrorResponse +// @Router /v1/enrollments/{id}/status [put] +func (h *EnrollmentHandler) UpdateEnrollmentStatus(c *gin.Context) { + enrollmentID := c.Param("id") + userRole := c.GetString("user_role") + + // Check if user has permission to update enrollment status + if userRole != string(types.UserRoleAdmin) && userRole != string(types.UserRoleInstructor) { + c.JSON(http.StatusForbidden, errors.NewAuthorizationError("insufficient_permissions", "Only admins and instructors can update enrollment status")) + return + } + + var req dto.UpdateEnrollmentStatusRequest + if err := c.ShouldBindJSON(&req); err != nil { + h.logger.Errorw("failed to bind request", "error", err) + c.JSON(http.StatusBadRequest, errors.NewValidationError("invalid_request", err.Error())) + return + } + + err := h.enrollmentSvc.UpdateEnrollmentStatus(c.Request.Context(), enrollmentID, types.EnrollmentStatus(req.Status)) + if err != nil { + h.logger.Errorw("failed to update enrollment status", "error", err, "enrollment_id", enrollmentID) + c.JSON(http.StatusInternalServerError, errors.NewInternalError("update_failed", "Failed to update enrollment status")) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "Enrollment status updated successfully"}) +} +``` + +### **4. Create DTOs** + +**File: `internal/api/dto/enrollment.go`** + +```go +package dto + +import ( + "time" + + "github.com/omkar273/codegeeky/internal/types" +) + +type ApplyInternshipRequest struct { + UserID string `json:"-"` // Set from auth context + InternshipID string `json:"-"` // Set from URL param + CoverLetter string `json:"cover_letter,omitempty"` + Resume string `json:"resume,omitempty"` + Portfolio string `json:"portfolio,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +type EnrollmentResponse struct { + EnrollmentID string `json:"enrollment_id"` + InternshipID string `json:"internship_id"` + PaymentID string `json:"payment_id"` + PaymentURL string `json:"payment_url"` + Status string `json:"status"` + CreatedAt time.Time `json:"created_at"` +} + +type UserApplicationResponse struct { + EnrollmentID string `json:"enrollment_id"` + InternshipID string `json:"internship_id"` + InternshipTitle string `json:"internship_title"` + CompanyName string `json:"company_name,omitempty"` + Status string `json:"status"` + PaymentStatus string `json:"payment_status"` + AppliedAt time.Time `json:"applied_at"` + EnrolledAt *time.Time `json:"enrolled_at,omitempty"` + InterviewDate *time.Time `json:"interview_date,omitempty"` + CompletedAt *time.Time `json:"completed_at,omitempty"` +} + +type UserApplicationsResponse struct { + Applications []*UserApplicationResponse `json:"applications"` + Pagination *types.Pagination `json:"pagination"` +} + +type PeerApplication struct { + UserID string `json:"user_id"` // Anonymized for others + Status string `json:"status"` + AppliedAt time.Time `json:"applied_at"` + IsYou bool `json:"is_you"` +} + +type ApplicationStats struct { + Total int `json:"total"` + Pending int `json:"pending"` + Enrolled int `json:"enrolled"` + Failed int `json:"failed"` +} + +type PeerApplicationsResponse struct { + InternshipID string `json:"internship_id"` + Stats ApplicationStats `json:"stats"` + Peers []*PeerApplication `json:"peers"` +} + +type UpdateEnrollmentStatusRequest struct { + Status string `json:"status" binding:"required"` + Reason string `json:"reason,omitempty"` + Notes string `json:"notes,omitempty"` + ScheduledAt *time.Time `json:"scheduled_at,omitempty"` // For interview scheduling +} +``` + +### **5. Update API Router** + +**File: `internal/api/router.go`** (Add to existing router) + +```go +// Add to the Handlers struct +type Handlers struct { + Health *v1.HealthHandler + Auth *v1.AuthHandler + User *v1.UserHandler + Internship *v1.InternshipHandler + Category *v1.CategoryHandler + Discount *v1.DiscountHandler + Enrollment *v1.EnrollmentHandler // Add this line +} + +// Add to the router function +func NewRouter(handlers *Handlers, cfg *config.Configuration, logger *logger.Logger) *gin.Engine { + // ... existing code ... + + // Enrollment routes + v1Enrollment := v1Router.Group("/enrollments") + v1Enrollment.Use(middleware.AuthenticateMiddleware(cfg, logger)) + { + v1Enrollment.PUT("/:id/status", handlers.Enrollment.UpdateEnrollmentStatus) + } + + // Add to user routes + v1Private.GET("/user/applications", handlers.Enrollment.GetUserApplications) + + // Add to internship routes + v1Internship.POST("/:id/apply", handlers.Enrollment.ApplyForInternship) + v1Internship.GET("/:id/peers", handlers.Enrollment.GetPeerApplications) + + return router +} +``` + +--- + +## ๐Ÿ“‹ Day 2: Payment Integration & Webhook Processing + +### **1. Enhanced Payment Webhook Handler** + +**File: `internal/webhook/subscriber/enrollment.go`** + +```go +package subscriber + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/service" + "github.com/omkar273/codegeeky/internal/types" +) + +type EnrollmentSubscriber struct { + logger *logger.Logger + enrollmentSvc *service.EnrollmentService +} + +func NewEnrollmentSubscriber( + logger *logger.Logger, + enrollmentSvc *service.EnrollmentService, +) *EnrollmentSubscriber { + return &EnrollmentSubscriber{ + logger: logger, + enrollmentSvc: enrollmentSvc, + } +} + +func (s *EnrollmentSubscriber) HandlePaymentSuccess(ctx context.Context, payload []byte) error { + var event dto.PaymentWebhookEvent + if err := json.Unmarshal(payload, &event); err != nil { + return fmt.Errorf("failed to unmarshal payment event: %w", err) + } + + s.logger.Infow("Processing payment success", "payment_id", event.PaymentID, "amount", event.Amount) + + // Process enrollment status update + err := s.enrollmentSvc.ProcessPaymentWebhook(ctx, event.PaymentID, types.PaymentStatusSucceeded) + if err != nil { + s.logger.Errorw("Failed to process payment webhook", "error", err, "payment_id", event.PaymentID) + return err + } + + return nil +} + +func (s *EnrollmentSubscriber) HandlePaymentFailure(ctx context.Context, payload []byte) error { + var event dto.PaymentWebhookEvent + if err := json.Unmarshal(payload, &event); err != nil { + return fmt.Errorf("failed to unmarshal payment event: %w", err) + } + + s.logger.Infow("Processing payment failure", "payment_id", event.PaymentID, "reason", event.FailureReason) + + err := s.enrollmentSvc.ProcessPaymentWebhook(ctx, event.PaymentID, types.PaymentStatusFailed) + if err != nil { + s.logger.Errorw("Failed to process payment failure webhook", "error", err, "payment_id", event.PaymentID) + return err + } + + return nil +} +``` + +### **2. Enhanced Razorpay Integration** + +**File: `internal/payment/providers/razorpay.go`** (Update existing) + +```go +// Add to existing RazorpayProvider struct +func (r *RazorpayProvider) ProcessWebhook(ctx context.Context, payload []byte, headers map[string]string) (*dto.WebhookResult, error) { + // Verify webhook signature + signature := headers["X-Razorpay-Signature"] + if !r.verifyWebhookSignature(payload, signature) { + return nil, fmt.Errorf("invalid webhook signature") + } + + var event map[string]interface{} + if err := json.Unmarshal(payload, &event); err != nil { + return nil, fmt.Errorf("failed to parse webhook: %w", err) + } + + eventType := event["event"].(string) + + // Extract payment data based on event type + var paymentData dto.PaymentWebhookEvent + + switch eventType { + case "payment.captured": + paymentEntity := event["payload"].(map[string]interface{})["payment"].(map[string]interface{})["entity"].(map[string]interface{}) + + paymentData = dto.PaymentWebhookEvent{ + EventID: event["id"].(string), + EventType: eventType, + PaymentID: paymentEntity["id"].(string), + Amount: paymentEntity["amount"].(float64) / 100, // Razorpay sends in paise + Currency: paymentEntity["currency"].(string), + Status: paymentEntity["status"].(string), + CreatedAt: time.Unix(int64(paymentEntity["created_at"].(float64)), 0), + } + + case "payment.failed": + paymentEntity := event["payload"].(map[string]interface{})["payment"].(map[string]interface{})["entity"].(map[string]interface{}) + + paymentData = dto.PaymentWebhookEvent{ + EventID: event["id"].(string), + EventType: eventType, + PaymentID: paymentEntity["id"].(string), + Amount: paymentEntity["amount"].(float64) / 100, + Currency: paymentEntity["currency"].(string), + Status: paymentEntity["status"].(string), + FailureReason: paymentEntity["error_description"].(string), + CreatedAt: time.Unix(int64(paymentEntity["created_at"].(float64)), 0), + } + } + + return &dto.WebhookResult{ + EventName: eventType, + EventID: paymentData.EventID, + Payload: paymentData, + Headers: headers, + Raw: event, + }, nil +} + +func (r *RazorpayProvider) verifyWebhookSignature(payload []byte, signature string) bool { + // Implement Razorpay webhook signature verification + // This is crucial for security + expectedSignature := r.generateWebhookSignature(payload) + return signature == expectedSignature +} + +func (r *RazorpayProvider) generateWebhookSignature(payload []byte) string { + // Implement HMAC-SHA256 signature generation using webhook secret + // Return the expected signature + return "" // Implement this +} +``` + +This implementation guide provides detailed, production-ready code for the enrollment system. Each component includes proper error handling, logging, security considerations, and follows Go best practices. + +The next sections would cover the communication system, notification system, and other components with similar detail. Would you like me to continue with the specific implementation guides for the remaining components? diff --git a/docs/prds/LMS_ENROLLMENT_WORKFLOW_PRD.md b/docs/prds/LMS_ENROLLMENT_WORKFLOW_PRD.md new file mode 100644 index 0000000..5eb0913 --- /dev/null +++ b/docs/prds/LMS_ENROLLMENT_WORKFLOW_PRD.md @@ -0,0 +1,971 @@ +# ๐ŸŽ“ LMS Course Enrollment Workflow with Payment Integration - PRD + +## ๐Ÿ“‹ Executive Summary + +This document outlines a comprehensive Product Requirements Document (PRD) for implementing a scalable LMS course enrollment workflow with integrated payment processing using Razorpay. The system is designed based on best practices from leading platforms like Udemy, Coursera, and LinkedIn Learning. + +### ๐ŸŽฏ Vision + +Create a seamless, scalable, and secure course enrollment experience that handles the complete user journey from course discovery to successful enrollment and payment completion. + +### ๐Ÿ” Current System Analysis + +Based on your existing architecture: + +- โœ… Enrollment schema with proper relationships (User โ†” Internship) +- โœ… Comprehensive payment system with domain models +- โœ… Razorpay integration foundation +- โœ… Webhook system for event processing +- โœ… Authorization/Authentication framework +- โœ… Repository pattern with Ent ORM + +--- + +## ๐Ÿ›๏ธ Platform Architecture Analysis + +### How Major Platforms Handle Enrollment Flows + +#### **1. Udemy's Approach** + +``` +Course Discovery โ†’ Add to Cart โ†’ Checkout โ†’ Payment โ†’ Enrollment โ†’ Course Access +``` + +- **Key Features:** + - Shopping cart functionality for multiple courses + - Coupon application at checkout + - Guest checkout with account creation post-payment + - Multiple payment methods (Cards, PayPal, Mobile wallets) + - Instant access upon successful payment + +#### **2. Coursera's Model** + +``` +Course Browse โ†’ Free Preview โ†’ Enrollment Decision โ†’ Payment/Financial Aid โ†’ Access +``` + +- **Key Features:** + - Free trial periods for specializations + - Subscription-based model with course bundles + - Financial aid application workflow + - Certificate purchase options + - Progress tracking from enrollment + +#### **3. LinkedIn Learning's Flow** + +``` +Course Discovery โ†’ Premium Check โ†’ Trial/Subscribe โ†’ Instant Access +``` + +- **Key Features:** + - Subscription-first model + - Seamless LinkedIn profile integration + - Corporate account management + - Bulk enrollments for enterprises + +### **Best Practices Identified:** + +1. **Progressive Disclosure**: Show essential info first, details later +2. **Multiple Payment Options**: Cater to different user preferences +3. **Clear Pricing**: Transparent cost breakdown with discounts +4. **Instant Gratification**: Immediate access post successful payment +5. **Error Recovery**: Graceful handling of payment failures +6. **Mobile Optimization**: Seamless mobile payment experience +7. **Trust Indicators**: Security badges, refund policies +8. **Abandoned Cart Recovery**: Email reminders for incomplete enrollments + +--- + +## ๐Ÿ”„ Complete Enrollment Workflow Design + +### **Phase 1: Pre-Enrollment (Course Discovery)** + +```mermaid +graph TD + A[User Discovers Course] --> B{Authenticated?} + B -->|No| C[Guest Browsing] + B -->|Yes| D[Personalized View] + C --> E[Course Details] + D --> E + E --> F[Prerequisites Check] + F --> G[Pricing Display] + G --> H[Enroll Button] +``` + +### **Phase 2: Enrollment Initialization** + +```mermaid +graph TD + A[User Clicks Enroll] --> B{Authentication Check} + B -->|Not Logged In| C[Login/Signup Flow] + B -->|Logged In| D[Enrollment Validation] + C --> D + D --> E{Already Enrolled?} + E -->|Yes| F[Redirect to Course] + E -->|No| G[Create Pending Enrollment] + G --> H[Initialize Payment] + H --> I[Redirect to Payment Gateway] +``` + +### **Phase 3: Payment Processing** + +```mermaid +graph TD + A[Payment Gateway] --> B{Payment Successful?} + B -->|Yes| C[Webhook: payment.success] + B -->|No| D[Webhook: payment.failed] + C --> E[Update Enrollment: enrolled] + D --> F[Update Enrollment: failed] + E --> G[Grant Course Access] + F --> H[Retry Payment Flow] + G --> I[Send Confirmation] + H --> J[Show Retry Options] +``` + +--- + +## ๐Ÿš€ API Workflow Definition + +### **API Call Sequence for Enrollment** + +#### **1. Course Information API** + +```http +GET /api/v1/courses/{courseId} +Authorization: Bearer {token} (optional) +``` + +**Response:** + +```json +{ + "course": { + "id": "int_01234567890", + "title": "Full Stack Development", + "price": 4999.0, + "currency": "INR", + "discount_price": 2999.0, + "has_active_discount": true, + "prerequisites": ["basic_programming"], + "duration_weeks": 12, + "enrollment_status": "not_enrolled|enrolled|pending", + "can_enroll": true, + "enrollment_deadline": "2024-03-30T23:59:59Z" + } +} +``` + +#### **2. Enrollment Initialization API** + +```http +POST /api/v1/enrollments/initialize +Authorization: Bearer {token} +Content-Type: application/json +``` + +**Request:** + +```json +{ + "course_id": "int_01234567890", + "coupon_code": "SAVE50", + "payment_method_preference": "razorpay", + "success_url": "https://app.example.com/enrollment/success", + "cancel_url": "https://app.example.com/enrollment/cancel", + "metadata": { + "source": "mobile_app", + "campaign": "spring_sale" + } +} +``` + +**Response:** + +```json +{ + "enrollment_id": "enr_01234567890", + "status": "pending", + "payment_required": true, + "final_amount": 2999.0, + "original_amount": 4999.0, + "discount_applied": 2000.0, + "currency": "INR", + "payment_session": { + "razorpay_order_id": "order_123456789", + "payment_url": "https://checkout.razorpay.com/...", + "expires_at": "2024-01-15T10:30:00Z" + }, + "idempotency_key": "enr_01234567890_20240115" +} +``` + +#### **3. Payment Status Check API** + +```http +GET /api/v1/enrollments/{enrollmentId}/payment-status +Authorization: Bearer {token} +``` + +**Response:** + +```json +{ + "enrollment_id": "enr_01234567890", + "enrollment_status": "enrolled", + "payment_status": "completed", + "payment_id": "pay_01234567890", + "razorpay_payment_id": "pay_razorpay123", + "completed_at": "2024-01-15T10:25:00Z", + "course_access_url": "/api/v1/courses/int_01234567890/access" +} +``` + +#### **4. Course Access Verification API** + +```http +GET /api/v1/courses/{courseId}/access +Authorization: Bearer {token} +``` + +**Response:** + +```json +{ + "has_access": true, + "enrollment_date": "2024-01-15T10:25:00Z", + "access_expires": null, + "course_progress": 0, + "next_lesson_url": "/api/v1/courses/int_01234567890/lessons/1" +} +``` + +--- + +## ๐Ÿ“Š Data Flow Architecture + +### **Enrollment Data Flow** + +#### **1. Initialization Data** + +```json +{ + "user": { + "id": "usr_01234567890", + "email": "user@example.com", + "full_name": "John Doe", + "phone": "+919876543210" + }, + "course": { + "id": "int_01234567890", + "title": "Full Stack Development", + "price": 4999.0, + "instructor": "Jane Smith" + }, + "pricing": { + "original_amount": 4999.0, + "discount_amount": 2000.0, + "final_amount": 2999.0, + "currency": "INR", + "tax_amount": 539.82, + "total_payable": 3538.82 + }, + "enrollment": { + "id": "enr_01234567890", + "status": "pending", + "expires_at": "2024-01-15T11:00:00Z" + } +} +``` + +#### **2. Payment Processing Data** + +```json +{ + "payment": { + "id": "pay_01234567890", + "idempotency_key": "enr_01234567890_20240115", + "amount": 3538.82, + "currency": "INR", + "destination_type": "enrollment", + "destination_id": "enr_01234567890", + "gateway_provider": "razorpay", + "status": "pending" + }, + "razorpay_order": { + "order_id": "order_123456789", + "amount": 353882, // in paise + "currency": "INR", + "notes": { + "enrollment_id": "enr_01234567890", + "course_id": "int_01234567890", + "user_id": "usr_01234567890" + } + } +} +``` + +--- + +## ๐ŸŽฏ Razorpay Integration Strategy + +### **1. Order Creation Flow** + +#### **Enhanced Razorpay Provider Implementation** + +```go +func (r *RazorpayProvider) CreateEnrollmentOrder(ctx context.Context, req *dto.EnrollmentPaymentRequest) (*dto.RazorpayOrderResponse, error) { + // Convert to paise (smallest currency unit) + amountInPaise := int(req.Amount.Mul(decimal.NewFromInt(100)).IntPart()) + + orderData := map[string]interface{}{ + "amount": amountInPaise, + "currency": req.Currency, + "receipt": req.IdempotencyKey, + "payment_capture": 1, // Auto capture + "notes": map[string]string{ + "enrollment_id": req.EnrollmentID, + "course_id": req.CourseID, + "user_id": req.UserID, + "user_email": req.UserEmail, + }, + } + + order, err := r.razorpayClient.Order.Create(orderData, nil) + if err != nil { + return nil, fmt.Errorf("razorpay order creation failed: %w", err) + } + + return &dto.RazorpayOrderResponse{ + OrderID: order["id"].(string), + Amount: amountInPaise, + Currency: req.Currency, + Key: r.config.KeyID, + Name: "Your Platform Name", + Description: fmt.Sprintf("Enrollment for %s", req.CourseName), + Image: "https://your-platform.com/logo.png", + CheckoutURL: r.generateCheckoutURL(order["id"].(string)), + CallbackURL: req.CallbackURL, + CancelURL: req.CancelURL, + }, nil +} +``` + +### **2. Webhook Handling Strategy** + +#### **Webhook Event Processing** + +```go +func (r *RazorpayProvider) ProcessWebhook(ctx context.Context, payload []byte, headers map[string]string) (*dto.WebhookResult, error) { + // Verify webhook signature + if !r.verifyWebhookSignature(payload, headers["x-razorpay-signature"]) { + return nil, errors.New("invalid webhook signature") + } + + var event map[string]interface{} + if err := json.Unmarshal(payload, &event); err != nil { + return nil, fmt.Errorf("failed to parse webhook: %w", err) + } + + eventType := event["event"].(string) + + switch eventType { + case "payment.captured": + return r.handlePaymentSuccess(event) + case "payment.failed": + return r.handlePaymentFailure(event) + case "order.paid": + return r.handleOrderCompletion(event) + default: + return &dto.WebhookResult{ + EventName: eventType, + Processed: false, + Message: "Event type not handled", + }, nil + } +} + +func (r *RazorpayProvider) handlePaymentSuccess(event map[string]interface{}) (*dto.WebhookResult, error) { + paymentData := event["payload"].(map[string]interface{})["payment"].(map[string]interface{}) + orderData := event["payload"].(map[string]interface{})["order"].(map[string]interface{}) + + // Extract enrollment information from order notes + notes := orderData["notes"].(map[string]interface{}) + enrollmentID := notes["enrollment_id"].(string) + + return &dto.WebhookResult{ + EventName: "payment.success", + EventID: event["id"].(string), + EnrollmentID: enrollmentID, + PaymentID: paymentData["id"].(string), + Amount: paymentData["amount"].(float64) / 100, // Convert from paise + Currency: paymentData["currency"].(string), + Processed: true, + Payload: event, + }, nil +} +``` + +### **3. Frontend Integration** + +#### **React/Next.js Integration Example** + +```javascript +// Enrollment initialization +const initializeEnrollment = async (courseId, couponCode = null) => { + const response = await fetch("/api/v1/enrollments/initialize", { + method: "POST", + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + course_id: courseId, + coupon_code: couponCode, + payment_method_preference: "razorpay", + success_url: `${window.location.origin}/enrollment/success`, + cancel_url: `${window.location.origin}/enrollment/cancel`, + }), + }); + + return response.json(); +}; + +// Razorpay payment processing +const processPayment = async (enrollmentData) => { + const options = { + key: enrollmentData.payment_session.razorpay_key, + order_id: enrollmentData.payment_session.razorpay_order_id, + amount: enrollmentData.final_amount * 100, // Convert to paise + currency: enrollmentData.currency, + name: "Your Platform Name", + description: `Enrollment for ${enrollmentData.course.title}`, + image: "/logo.png", + handler: async function (response) { + // Payment successful + await verifyPayment({ + razorpay_payment_id: response.razorpay_payment_id, + razorpay_order_id: response.razorpay_order_id, + razorpay_signature: response.razorpay_signature, + enrollment_id: enrollmentData.enrollment_id, + }); + }, + prefill: { + name: enrollmentData.user.full_name, + email: enrollmentData.user.email, + contact: enrollmentData.user.phone, + }, + theme: { + color: "#3399cc", + }, + modal: { + ondismiss: function () { + // Handle payment cancellation + handlePaymentCancellation(enrollmentData.enrollment_id); + }, + }, + }; + + const rzp = new Razorpay(options); + rzp.open(); +}; +``` + +--- + +## ๐Ÿ”ง Implementation Guidelines + +### **1. Service Layer Architecture** + +#### **Enhanced Enrollment Service** + +```go +type EnrollmentService interface { + InitializeEnrollment(ctx context.Context, req *dto.InitializeEnrollmentRequest) (*dto.EnrollmentResponse, error) + ConfirmEnrollment(ctx context.Context, enrollmentID string, paymentDetails *dto.PaymentConfirmation) error + GetEnrollmentStatus(ctx context.Context, enrollmentID string) (*dto.EnrollmentStatusResponse, error) + CancelEnrollment(ctx context.Context, enrollmentID string, reason string) error + RefundEnrollment(ctx context.Context, enrollmentID string, req *dto.RefundRequest) error + ListUserEnrollments(ctx context.Context, userID string, filter *types.EnrollmentFilter) (*dto.EnrollmentListResponse, error) +} + +type enrollmentService struct { + ServiceParams ServiceParams + PaymentService PaymentService + DiscountService DiscountService + NotificationSvc NotificationService +} + +func (s *enrollmentService) InitializeEnrollment(ctx context.Context, req *dto.InitializeEnrollmentRequest) (*dto.EnrollmentResponse, error) { + // 1. Validate request and user permissions + if err := s.validateEnrollmentRequest(ctx, req); err != nil { + return nil, err + } + + // 2. Check for existing enrollment + existingEnrollment, err := s.ServiceParams.EnrollmentRepo.GetByUserAndCourse(ctx, req.UserID, req.CourseID) + if err != nil && !ierr.IsNotFound(err) { + return nil, err + } + if existingEnrollment != nil { + return s.handleExistingEnrollment(ctx, existingEnrollment) + } + + // 3. Get course details and validate availability + course, err := s.ServiceParams.InternshipRepo.Get(ctx, req.CourseID) + if err != nil { + return nil, err + } + + // 4. Calculate pricing with discounts + pricing, err := s.calculatePricing(ctx, course, req.CouponCode, req.UserID) + if err != nil { + return nil, err + } + + // 5. Create enrollment record + enrollment := &domainEnrollment.Enrollment{ + ID: types.GenerateUUIDWithPrefix(types.UUID_PREFIX_ENROLLMENT), + UserID: req.UserID, + InternshipID: req.CourseID, + EnrollmentStatus: types.EnrollmentStatusPending, + PaymentStatus: types.PaymentStatusPending, + BaseModel: types.BaseModel{ + Status: types.StatusPublished, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + CreatedBy: req.UserID, + UpdatedBy: req.UserID, + }, + } + + var paymentResponse *dto.PaymentResponse + + err = s.ServiceParams.DB.WithTx(ctx, func(ctx context.Context) error { + // Create enrollment + if err := s.ServiceParams.EnrollmentRepo.Create(ctx, enrollment); err != nil { + return err + } + + // Handle free courses + if pricing.FinalAmount.IsZero() { + enrollment.EnrollmentStatus = types.EnrollmentStatusEnrolled + enrollment.PaymentStatus = types.PaymentStatusNotRequired + enrollment.EnrolledAt = lo.ToPtr(time.Now()) + return s.ServiceParams.EnrollmentRepo.Update(ctx, enrollment) + } + + // Create payment for paid courses + paymentReq := &dto.CreatePaymentRequest{ + IdempotencyKey: fmt.Sprintf("%s_%d", enrollment.ID, time.Now().Unix()), + DestinationType: types.PaymentDestinationTypeEnrollment, + DestinationID: enrollment.ID, + PaymentGatewayProvider: types.PaymentGatewayProviderRazorpay, + Amount: pricing.FinalAmount, + Currency: types.Currency(course.Currency), + PaymentMethodType: lo.ToPtr(types.PaymentMethodTypeOnline), + PaymentMethodID: "razorpay_checkout", + TrackAttempts: true, + SuccessURL: req.SuccessURL, + CancelURL: req.CancelURL, + Metadata: map[string]string{ + "enrollment_id": enrollment.ID, + "course_id": req.CourseID, + "user_id": req.UserID, + "course_title": course.Title, + }, + } + + paymentResp, err := s.PaymentService.Create(ctx, paymentReq) + if err != nil { + return fmt.Errorf("failed to create payment: %w", err) + } + + // Link payment to enrollment + enrollment.PaymentID = lo.ToPtr(paymentResp.Payment.ID) + if err := s.ServiceParams.EnrollmentRepo.Update(ctx, enrollment); err != nil { + return fmt.Errorf("failed to link payment to enrollment: %w", err) + } + + paymentResponse = paymentResp + return nil + }) + + if err != nil { + return nil, err + } + + // 6. Prepare response + response := &dto.EnrollmentResponse{ + EnrollmentID: enrollment.ID, + Status: string(enrollment.EnrollmentStatus), + PaymentRequired: !pricing.FinalAmount.IsZero(), + Pricing: pricing, + } + + if paymentResponse != nil { + response.PaymentSession = &dto.PaymentSessionInfo{ + PaymentID: paymentResponse.Payment.ID, + RazorpayOrderID: paymentResponse.GatewayResponse.ProviderPaymentID, + PaymentURL: paymentResponse.GatewayResponse.RedirectURL, + ExpiresAt: time.Now().Add(30 * time.Minute), + } + } + + return response, nil +} +``` + +### **2. Error Handling Strategy** + +#### **Comprehensive Error Management** + +```go +// Custom enrollment errors +var ( + ErrAlreadyEnrolled = ierr.NewError("already_enrolled"). + WithHint("User is already enrolled in this course"). + Mark(ierr.ErrValidation) + + ErrCourseNotAvailable = ierr.NewError("course_not_available"). + WithHint("Course is not available for enrollment"). + Mark(ierr.ErrValidation) + + ErrEnrollmentExpired = ierr.NewError("enrollment_expired"). + WithHint("Enrollment session has expired"). + Mark(ierr.ErrValidation) + + ErrPaymentRequired = ierr.NewError("payment_required"). + WithHint("Payment is required to complete enrollment"). + Mark(ierr.ErrValidation) +) + +// Error recovery strategies +func (s *enrollmentService) handlePaymentFailure(ctx context.Context, enrollmentID string, failure *dto.PaymentFailure) error { + enrollment, err := s.ServiceParams.EnrollmentRepo.Get(ctx, enrollmentID) + if err != nil { + return err + } + + // Update enrollment status + enrollment.EnrollmentStatus = types.EnrollmentStatusFailed + enrollment.PaymentStatus = types.PaymentStatusFailed + + if err := s.ServiceParams.EnrollmentRepo.Update(ctx, enrollment); err != nil { + return err + } + + // Send failure notification + go func() { + s.NotificationSvc.SendEnrollmentFailureNotification(context.Background(), &dto.EnrollmentFailureNotification{ + UserID: enrollment.UserID, + EnrollmentID: enrollmentID, + CourseID: enrollment.InternshipID, + Reason: failure.Reason, + RetryURL: s.generateRetryURL(enrollmentID), + }) + }() + + return nil +} +``` + +### **3. Database Optimizations** + +#### **Indexing Strategy** + +```sql +-- Enrollment performance indexes +CREATE INDEX CONCURRENTLY idx_enrollments_user_status ON enrollments(user_id, enrollment_status) WHERE status != 'deleted'; +CREATE INDEX CONCURRENTLY idx_enrollments_course_status ON enrollments(internship_id, enrollment_status) WHERE status != 'deleted'; +CREATE INDEX CONCURRENTLY idx_enrollments_payment_id ON enrollments(payment_id) WHERE payment_id IS NOT NULL; +CREATE INDEX CONCURRENTLY idx_enrollments_created_at ON enrollments(created_at DESC); + +-- Payment performance indexes +CREATE INDEX CONCURRENTLY idx_payments_destination ON payments(destination_type, destination_id); +CREATE INDEX CONCURRENTLY idx_payments_gateway_status ON payments(payment_gateway_provider, payment_status); +CREATE INDEX CONCURRENTLY idx_payments_user_status ON payments(created_by, payment_status); + +-- Compound indexes for common queries +CREATE INDEX CONCURRENTLY idx_enrollments_user_course_unique ON enrollments(user_id, internship_id) WHERE status != 'deleted'; +``` + +--- + +## ๐Ÿ“ฑ Mobile Considerations + +### **Mobile-First Design Principles** + +#### **1. Progressive Web App (PWA) Support** + +```javascript +// Service Worker for offline enrollment handling +self.addEventListener("sync", function (event) { + if (event.tag === "enrollment-retry") { + event.waitUntil(retryFailedEnrollments()); + } +}); + +const retryFailedEnrollments = async () => { + const failedEnrollments = await getFailedEnrollments(); + for (const enrollment of failedEnrollments) { + try { + await retryEnrollment(enrollment); + } catch (error) { + console.error("Retry failed:", error); + } + } +}; +``` + +#### **2. Mobile Payment Optimization** + +```javascript +// Mobile-optimized payment flow +const initializeMobilePayment = async (enrollmentData) => { + // Check if running in mobile app + if (isMobileApp()) { + return initializeInAppPayment(enrollmentData); + } + + // Web-based mobile payment + const options = { + ...enrollmentData.payment_session, + modal: { + backdrop_close: false, // Prevent accidental dismissal + confirm_close: true, + animation: true, + }, + config: { + display: { + language: getUserLanguage(), + hide: ["contact", "email"], // Pre-filled from profile + }, + }, + }; + + return processRazorpayPayment(options); +}; +``` + +--- + +## ๐Ÿ”’ Security & Compliance + +### **Security Measures** + +#### **1. Payment Security** + +```go +// PCI DSS compliance measures +type PaymentSecurityManager struct { + encryptor security.Encryptor + auditor audit.AuditLogger +} + +func (p *PaymentSecurityManager) SecurePaymentData(data *dto.PaymentData) (*dto.SecuredPaymentData, error) { + // Encrypt sensitive data + encryptedCard, err := p.encryptor.Encrypt(data.CardNumber) + if err != nil { + return nil, err + } + + // Log payment attempt for audit + p.auditor.LogPaymentAttempt(audit.PaymentAttemptLog{ + UserID: data.UserID, + Amount: data.Amount, + Currency: data.Currency, + Timestamp: time.Now(), + IPAddress: data.IPAddress, + }) + + return &dto.SecuredPaymentData{ + EncryptedCard: encryptedCard, + TokenizedData: data.TokenizedData, + AuditID: p.auditor.GetLastAuditID(), + }, nil +} +``` + +#### **2. Fraud Prevention** + +```go +// Fraud detection service +type FraudDetectionService struct { + riskEngine RiskEngine + geoValidator GeoValidator + velocityCheck VelocityChecker +} + +func (f *FraudDetectionService) AssessEnrollmentRisk(ctx context.Context, req *dto.EnrollmentRequest) (*dto.RiskAssessment, error) { + assessment := &dto.RiskAssessment{ + UserID: req.UserID, + RequestID: req.RequestID, + Timestamp: time.Now(), + } + + // Check user behavior patterns + riskScore := f.riskEngine.CalculateUserRisk(req.UserID) + assessment.UserRiskScore = riskScore + + // Validate geographic consistency + geoRisk := f.geoValidator.ValidateLocation(req.IPAddress, req.UserID) + assessment.GeographicRisk = geoRisk + + // Check payment velocity + velocityRisk := f.velocityCheck.CheckPaymentVelocity(req.UserID, req.Amount) + assessment.VelocityRisk = velocityRisk + + // Calculate overall risk + assessment.OverallRisk = (riskScore + geoRisk + velocityRisk) / 3 + assessment.RecommendedAction = f.getRecommendedAction(assessment.OverallRisk) + + return assessment, nil +} +``` + +--- + +## ๐Ÿ“Š Analytics & Monitoring + +### **Key Metrics to Track** + +#### **1. Enrollment Funnel Metrics** + +```go +type EnrollmentAnalytics struct { + CourseViews int64 `json:"course_views"` + EnrollmentInitiations int64 `json:"enrollment_initiations"` + PaymentInitiations int64 `json:"payment_initiations"` + PaymentCompletions int64 `json:"payment_completions"` + EnrollmentCompletions int64 `json:"enrollment_completions"` + + ConversionRates struct { + ViewToEnrollment float64 `json:"view_to_enrollment"` + EnrollmentToPayment float64 `json:"enrollment_to_payment"` + PaymentToCompletion float64 `json:"payment_to_completion"` + OverallConversion float64 `json:"overall_conversion"` + } `json:"conversion_rates"` +} +``` + +#### **2. Performance Monitoring** + +```go +// Metrics collection +func (s *enrollmentService) recordMetrics(ctx context.Context, operation string, duration time.Duration, success bool) { + labels := map[string]string{ + "operation": operation, + "success": strconv.FormatBool(success), + } + + // Record operation duration + s.metrics.RecordDuration("enrollment_operation_duration", duration, labels) + + // Record operation count + s.metrics.IncrementCounter("enrollment_operations_total", labels) + + // Record success rate + if success { + s.metrics.IncrementCounter("enrollment_operations_success_total", labels) + } else { + s.metrics.IncrementCounter("enrollment_operations_failure_total", labels) + } +} +``` + +--- + +## ๐Ÿš€ Deployment Strategy + +### **Phased Rollout Plan** + +#### **Phase 1: Basic Enrollment (Week 1-2)** + +- โœ… Course enrollment API +- โœ… Basic Razorpay integration +- โœ… Webhook handling +- โœ… Database schema updates + +#### **Phase 2: Enhanced Features (Week 3-4)** + +- โœ… Discount/coupon system +- โœ… Multiple payment methods +- โœ… Mobile optimization +- โœ… Error handling improvements + +#### **Phase 3: Advanced Features (Week 5-6)** + +- โœ… Analytics dashboard +- โœ… Fraud prevention +- โœ… Performance optimization +- โœ… Monitoring & alerting + +#### **Phase 4: Scale & Optimize (Week 7-8)** + +- โœ… Load testing +- โœ… Performance tuning +- โœ… Security audit +- โœ… Production deployment + +--- + +## ๐Ÿ“š Implementation Checklist + +### **Backend Implementation** + +- [ ] Update enrollment schema with payment linkage +- [ ] Implement enrollment service with transaction management +- [ ] Enhanced Razorpay provider with order creation +- [ ] Webhook processing for payment events +- [ ] Error handling and retry mechanisms +- [ ] Analytics and metrics collection +- [ ] Security measures and fraud prevention + +### **Frontend Implementation** + +- [ ] Enrollment flow UI components +- [ ] Razorpay integration with checkout +- [ ] Payment status polling +- [ ] Error handling and user feedback +- [ ] Mobile-responsive design +- [ ] Offline support (PWA) + +### **DevOps & Monitoring** + +- [ ] Database migration scripts +- [ ] API documentation updates +- [ ] Monitoring dashboards +- [ ] Alert configurations +- [ ] Load testing setup +- [ ] Deployment automation + +### **Testing Strategy** + +- [ ] Unit tests for services +- [ ] Integration tests for payment flow +- [ ] End-to-end enrollment testing +- [ ] Load testing for scalability +- [ ] Security penetration testing +- [ ] Mobile app testing + +--- + +## ๐ŸŽฏ Success Criteria + +### **Technical KPIs** + +- **API Response Time**: < 200ms for enrollment APIs +- **Payment Success Rate**: > 95% for all payment methods +- **System Uptime**: 99.9% availability +- **Error Rate**: < 1% for critical enrollment flows + +### **Business KPIs** + +- **Enrollment Conversion**: > 15% from course view to enrollment +- **Payment Abandonment**: < 10% at payment step +- **User Satisfaction**: > 4.5/5 rating for enrollment experience +- **Revenue Impact**: Measurable increase in course enrollments + +--- + +This PRD provides a comprehensive foundation for implementing a scalable, secure, and user-friendly course enrollment system with integrated payment processing. The design leverages your existing architecture while incorporating industry best practices from leading LMS platforms. diff --git a/docs/prds/PAYMENT_SYSTEM_IMPLEMENTATION_GUIDE.md b/docs/prds/PAYMENT_SYSTEM_IMPLEMENTATION_GUIDE.md new file mode 100644 index 0000000..6a6bbae --- /dev/null +++ b/docs/prds/PAYMENT_SYSTEM_IMPLEMENTATION_GUIDE.md @@ -0,0 +1,751 @@ +# ๐Ÿ› ๏ธ Payment System Implementation Guide + +## ๐Ÿ“‹ Architecture Overview + +Based on research of platforms like Udemy, Coursera, and LinkedIn Learning, here's a comprehensive implementation guide for a flexible payment system. + +--- + +## ๐Ÿ—๏ธ Core System Architecture + +### **Design Patterns Implementation** + +#### **1. Strategy Pattern - Payment Gateway Management** + +```go +// File: internal/payment/gateway/interface.go +package gateway + +import ( + "context" + "github.com/omkar273/codegeeky/internal/types" +) + +type PaymentGateway interface { + Name() string + ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) + ProcessRefund(ctx context.Context, req *RefundRequest) (*RefundResponse, error) + ValidateWebhook(payload []byte, signature string) (*WebhookEvent, error) + GetSupportedMethods() []types.PaymentMethod + HealthCheck(ctx context.Context) error +} + +type PaymentRequest struct { + Amount types.Money `json:"amount"` + Currency string `json:"currency"` + UserID string `json:"user_id"` + OrderID string `json:"order_id"` + PaymentMethod types.PaymentMethod `json:"payment_method"` + PaymentDetails map[string]interface{} `json:"payment_details"` + ReturnURL string `json:"return_url"` + CancelURL string `json:"cancel_url"` + Metadata map[string]string `json:"metadata"` +} + +type PaymentResponse struct { + TransactionID string `json:"transaction_id"` + Status types.PaymentStatus `json:"status"` + RedirectURL string `json:"redirect_url,omitempty"` + PaymentMethod types.PaymentMethod `json:"payment_method"` + GatewayData map[string]interface{} `json:"gateway_data,omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` +} +``` + +#### **2. Factory Pattern - Gateway Creation** + +```go +// File: internal/payment/gateway/factory.go +package gateway + +import ( + "fmt" + "github.com/omkar273/codegeeky/internal/config" +) + +type GatewayFactory interface { + CreateGateway(gatewayType string, cfg *config.GatewayConfig) (PaymentGateway, error) +} + +type ConcreteGatewayFactory struct{} + +func (f *ConcreteGatewayFactory) CreateGateway(gatewayType string, cfg *config.GatewayConfig) (PaymentGateway, error) { + switch gatewayType { + case "stripe": + return NewStripeGateway(cfg.Stripe) + case "razorpay": + return NewRazorpayGateway(cfg.Razorpay) + case "paypal": + return NewPayPalGateway(cfg.PayPal) + default: + return nil, fmt.Errorf("unsupported gateway type: %s", gatewayType) + } +} +``` + +#### **3. Gateway Manager with Load Balancing** + +```go +// File: internal/payment/manager.go +package payment + +import ( + "context" + "fmt" + "sync" + "github.com/omkar273/codegeeky/internal/payment/gateway" + "github.com/omkar273/codegeeky/internal/logger" +) + +type GatewayManager struct { + gateways map[string]gateway.PaymentGateway + selector GatewaySelector + health *HealthMonitor + mu sync.RWMutex + logger *logger.Logger +} + +func NewGatewayManager(logger *logger.Logger) *GatewayManager { + return &GatewayManager{ + gateways: make(map[string]gateway.PaymentGateway), + selector: NewSmartGatewaySelector(), + health: NewHealthMonitor(logger), + logger: logger, + } +} + +func (m *GatewayManager) RegisterGateway(name string, gw gateway.PaymentGateway) { + m.mu.Lock() + defer m.mu.Unlock() + + m.gateways[name] = gw + m.health.Monitor(name, gw) + m.logger.Infow("Gateway registered", "gateway", name) +} + +func (m *GatewayManager) ProcessPayment(ctx context.Context, req *gateway.PaymentRequest) (*gateway.PaymentResponse, error) { + selectedGateway, err := m.selector.SelectGateway(req, m.gateways) + if err != nil { + return nil, fmt.Errorf("gateway selection failed: %w", err) + } + + m.logger.Infow("Processing payment", + "gateway", selectedGateway.Name(), + "amount", req.Amount, + "method", req.PaymentMethod) + + return selectedGateway.ProcessPayment(ctx, req) +} +``` + +--- + +## ๐Ÿ”ง Implementation Components + +### **1. Enrollment Service with Payment Integration** + +```go +// File: internal/service/enrollment.go +package service + +import ( + "context" + "fmt" + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/domain/enrollment" + "github.com/omkar273/codegeeky/internal/payment" + "github.com/omkar273/codegeeky/internal/types" +) + +type EnrollmentService struct { + paymentManager *payment.GatewayManager + enrollRepo enrollment.Repository + discountSvc *DiscountService + notificationSvc *NotificationService + eventPublisher *EventPublisher + logger *logger.Logger +} + +func (s *EnrollmentService) ProcessEnrollment(ctx context.Context, req *dto.EnrollmentRequest) (*dto.EnrollmentResponse, error) { + // 1. Validate request + if err := s.validateEnrollmentRequest(ctx, req); err != nil { + return nil, err + } + + // 2. Apply discounts + finalAmount, discounts, err := s.discountSvc.CalculateDiscount(ctx, &discount.CalculateRequest{ + OriginalAmount: req.Amount, + CouponCodes: req.CouponCodes, + UserID: req.UserID, + InternshipID: req.InternshipID, + }) + if err != nil { + return nil, fmt.Errorf("discount calculation failed: %w", err) + } + + // 3. Create enrollment record + enrollment, err := s.enrollRepo.Create(ctx, &enrollment.CreateRequest{ + UserID: req.UserID, + InternshipID: req.InternshipID, + OriginalAmount: req.Amount, + DiscountAmount: req.Amount.Sub(finalAmount), + FinalAmount: finalAmount, + Status: types.EnrollmentStatusPending, + AppliedDiscounts: discounts, + }) + if err != nil { + return nil, fmt.Errorf("enrollment creation failed: %w", err) + } + + // 4. Process payment + paymentReq := &gateway.PaymentRequest{ + Amount: finalAmount, + Currency: req.Currency, + UserID: req.UserID, + OrderID: enrollment.ID, + PaymentMethod: req.PaymentMethod, + PaymentDetails: req.PaymentDetails, + ReturnURL: req.ReturnURL, + CancelURL: req.CancelURL, + Metadata: map[string]string{ + "enrollment_id": enrollment.ID, + "internship_id": req.InternshipID, + "type": "enrollment", + }, + } + + paymentResp, err := s.paymentManager.ProcessPayment(ctx, paymentReq) + if err != nil { + // Update enrollment to failed + s.enrollRepo.UpdateStatus(ctx, enrollment.ID, types.EnrollmentStatusFailed) + return nil, fmt.Errorf("payment processing failed: %w", err) + } + + // 5. Update enrollment with payment info + enrollment.PaymentID = &paymentResp.TransactionID + enrollment.Status = types.EnrollmentStatusPaymentPending + if err := s.enrollRepo.Update(ctx, enrollment); err != nil { + return nil, fmt.Errorf("enrollment update failed: %w", err) + } + + // 6. Send notifications + go s.sendEnrollmentNotifications(ctx, enrollment, paymentResp) + + // 7. Publish event + s.eventPublisher.PublishEnrollmentCreated(ctx, enrollment) + + return &dto.EnrollmentResponse{ + EnrollmentID: enrollment.ID, + PaymentID: paymentResp.TransactionID, + Status: string(enrollment.Status), + RedirectURL: paymentResp.RedirectURL, + FinalAmount: finalAmount, + DiscountAmount: req.Amount.Sub(finalAmount), + }, nil +} +``` + +### **2. Flexible Discount System** + +```go +// File: internal/service/discount.go +package service + +import ( + "context" + "github.com/omkar273/codegeeky/internal/types" +) + +type DiscountService struct { + discountRepo discount.Repository + calculator *DiscountCalculator + validator *DiscountValidator +} + +type DiscountCalculator struct { + rules []DiscountRule +} + +func (c *DiscountCalculator) CalculateDiscount(ctx context.Context, req *CalculateRequest) (types.Money, []*AppliedDiscount, error) { + result := &DiscountResult{ + OriginalAmount: req.OriginalAmount, + FinalAmount: req.OriginalAmount, + AppliedDiscounts: make([]*AppliedDiscount, 0), + } + + // Get applicable discounts + discounts, err := c.discountRepo.GetApplicableDiscounts(ctx, &discount.Query{ + CouponCodes: req.CouponCodes, + UserID: req.UserID, + InternshipID: req.InternshipID, + Amount: req.OriginalAmount, + }) + if err != nil { + return req.OriginalAmount, nil, err + } + + // Apply discounts in order of priority + for _, discount := range discounts { + if !c.validator.IsValidForApplication(discount, req) { + continue + } + + applied := c.applyDiscount(result.FinalAmount, discount) + if applied.Amount.GreaterThan(types.ZeroMoney) { + result.AppliedDiscounts = append(result.AppliedDiscounts, applied) + result.FinalAmount = result.FinalAmount.Sub(applied.Amount) + + // Stop if not stackable + if !discount.Stackable { + break + } + } + } + + return result.FinalAmount, result.AppliedDiscounts, nil +} + +func (c *DiscountCalculator) applyDiscount(amount types.Money, discount *types.Discount) *AppliedDiscount { + var discountAmount types.Money + + switch discount.Type { + case types.DiscountTypePercentage: + discountAmount = amount.Mul(discount.Value).Div(types.NewMoney(100)) + case types.DiscountTypeFixedAmount: + discountAmount = discount.ValueMoney + case types.DiscountTypeBOGO: + // Buy one get one logic + discountAmount = amount.Div(types.NewMoney(2)) + } + + // Apply maximum discount limit + if discount.MaxDiscountAmount != nil && discountAmount.GreaterThan(*discount.MaxDiscountAmount) { + discountAmount = *discount.MaxDiscountAmount + } + + // Ensure we don't discount more than the amount + if discountAmount.GreaterThan(amount) { + discountAmount = amount + } + + return &AppliedDiscount{ + DiscountID: discount.ID, + Code: discount.Code, + Type: discount.Type, + Amount: discountAmount, + } +} +``` + +### **3. Multi-Gateway Payment Adapters** + +#### **Stripe Adapter** + +```go +// File: internal/payment/adapters/stripe.go +package adapters + +import ( + "context" + "fmt" + "github.com/stripe/stripe-go/v72" + "github.com/stripe/stripe-go/v72/paymentintent" +) + +type StripeAdapter struct { + client *stripe.Client + config *StripeConfig +} + +func (s *StripeAdapter) ProcessPayment(ctx context.Context, req *gateway.PaymentRequest) (*gateway.PaymentResponse, error) { + params := &stripe.PaymentIntentParams{ + Amount: stripe.Int64(req.Amount.ToCents()), + Currency: stripe.String(strings.ToLower(req.Currency)), + AutomaticPaymentMethods: &stripe.PaymentIntentAutomaticPaymentMethodsParams{ + Enabled: stripe.Bool(true), + }, + Metadata: req.Metadata, + } + + // Add customer if exists + if req.UserID != "" { + customer, err := s.getOrCreateCustomer(ctx, req.UserID) + if err == nil { + params.Customer = stripe.String(customer.ID) + } + } + + // Handle different payment methods + switch req.PaymentMethod { + case types.PaymentMethodCard: + // Card payments handled by automatic payment methods + case types.PaymentMethodUPI: + params.PaymentMethodTypes = stripe.StringSlice([]string{"upi"}) + case types.PaymentMethodWallet: + params.PaymentMethodTypes = stripe.StringSlice([]string{"link", "paypal"}) + } + + pi, err := paymentintent.New(params) + if err != nil { + return nil, fmt.Errorf("stripe payment intent creation failed: %w", err) + } + + return &gateway.PaymentResponse{ + TransactionID: pi.ID, + Status: s.mapStripeStatus(pi.Status), + PaymentMethod: req.PaymentMethod, + GatewayData: map[string]interface{}{ + "client_secret": pi.ClientSecret, + "payment_intent_id": pi.ID, + }, + }, nil +} +``` + +#### **Razorpay Adapter** + +```go +// File: internal/payment/adapters/razorpay.go +package adapters + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" +) + +type RazorpayAdapter struct { + client *razorpay.Client + config *RazorpayConfig +} + +func (r *RazorpayAdapter) ProcessPayment(ctx context.Context, req *gateway.PaymentRequest) (*gateway.PaymentResponse, error) { + orderReq := map[string]interface{}{ + "amount": req.Amount.ToCents(), // Razorpay uses paise + "currency": req.Currency, + "receipt": req.OrderID, + "notes": req.Metadata, + } + + order, err := r.client.Order.Create(orderReq, nil) + if err != nil { + return nil, fmt.Errorf("razorpay order creation failed: %w", err) + } + + checkoutOptions := map[string]interface{}{ + "key": r.config.KeyID, + "amount": req.Amount.ToCents(), + "currency": req.Currency, + "order_id": order["id"], + "callback_url": req.ReturnURL, + "cancel_url": req.CancelURL, + "prefill": map[string]interface{}{ + "contact": req.PaymentDetails["phone"], + "email": req.PaymentDetails["email"], + }, + "theme": map[string]interface{}{ + "color": "#3399cc", + }, + } + + // Handle UPI payments + if req.PaymentMethod == types.PaymentMethodUPI { + checkoutOptions["method"] = map[string]interface{}{ + "upi": true, + } + } + + checkoutURL := r.generateCheckoutURL(checkoutOptions) + + return &gateway.PaymentResponse{ + TransactionID: order["id"].(string), + Status: types.PaymentStatusPending, + RedirectURL: checkoutURL, + PaymentMethod: req.PaymentMethod, + GatewayData: map[string]interface{}{ + "order_id": order["id"], + "key_id": r.config.KeyID, + }, + }, nil +} + +func (r *RazorpayAdapter) ValidateWebhook(payload []byte, signature string) (*gateway.WebhookEvent, error) { + expectedSignature := r.generateWebhookSignature(payload) + if !hmac.Equal([]byte(signature), []byte(expectedSignature)) { + return nil, fmt.Errorf("invalid webhook signature") + } + + var event map[string]interface{} + if err := json.Unmarshal(payload, &event); err != nil { + return nil, fmt.Errorf("invalid webhook payload: %w", err) + } + + return &gateway.WebhookEvent{ + EventType: event["event"].(string), + Data: event, + }, nil +} + +func (r *RazorpayAdapter) generateWebhookSignature(payload []byte) string { + h := hmac.New(sha256.New, []byte(r.config.WebhookSecret)) + h.Write(payload) + return hex.EncodeToString(h.Sum(nil)) +} +``` + +### **4. Comprehensive Webhook System** + +```go +// File: internal/webhook/processor.go +package webhook + +import ( + "context" + "encoding/json" + "fmt" + "github.com/omkar273/codegeeky/internal/payment/gateway" +) + +type WebhookProcessor struct { + gatewayManager *payment.GatewayManager + enrollmentSvc *service.EnrollmentService + paymentRepo payment.Repository + logger *logger.Logger +} + +func (p *WebhookProcessor) ProcessWebhook(ctx context.Context, req *WebhookRequest) error { + // 1. Identify gateway + gateway, err := p.gatewayManager.GetGateway(req.GatewayName) + if err != nil { + return fmt.Errorf("unknown gateway: %w", err) + } + + // 2. Validate webhook + event, err := gateway.ValidateWebhook(req.Payload, req.Signature) + if err != nil { + return fmt.Errorf("webhook validation failed: %w", err) + } + + // 3. Process based on event type + switch event.EventType { + case "payment.captured", "payment_intent.succeeded": + return p.processPaymentSuccess(ctx, event) + case "payment.failed", "payment_intent.payment_failed": + return p.processPaymentFailure(ctx, event) + case "refund.processed": + return p.processRefund(ctx, event) + default: + p.logger.Warnw("Unhandled webhook event", "event_type", event.EventType) + return nil + } +} + +func (p *WebhookProcessor) processPaymentSuccess(ctx context.Context, event *gateway.WebhookEvent) error { + transactionID := p.extractTransactionID(event) + + // Update payment status + payment, err := p.paymentRepo.GetByTransactionID(ctx, transactionID) + if err != nil { + return fmt.Errorf("payment not found: %w", err) + } + + payment.Status = types.PaymentStatusSucceeded + payment.ProcessedAt = time.Now() + + if err := p.paymentRepo.Update(ctx, payment); err != nil { + return fmt.Errorf("payment update failed: %w", err) + } + + // Update enrollment status + if payment.EnrollmentID != nil { + err := p.enrollmentSvc.CompleteEnrollment(ctx, *payment.EnrollmentID) + if err != nil { + p.logger.Errorw("Failed to complete enrollment", + "enrollment_id", *payment.EnrollmentID, + "error", err) + } + } + + return nil +} +``` + +### **5. Advanced Reporting System** + +```go +// File: internal/service/analytics.go +package service + +import ( + "context" + "time" +) + +type AnalyticsService struct { + paymentRepo payment.Repository + cache cache.Cache + logger *logger.Logger +} + +func (s *AnalyticsService) GetPaymentAnalytics(ctx context.Context, req *AnalyticsRequest) (*PaymentAnalytics, error) { + cacheKey := fmt.Sprintf("analytics:%s:%s:%s", req.StartDate, req.EndDate, req.GroupBy) + + // Try cache first + if cached, err := s.cache.Get(ctx, cacheKey); err == nil { + var analytics PaymentAnalytics + if json.Unmarshal(cached, &analytics) == nil { + return &analytics, nil + } + } + + // Query database + analytics := &PaymentAnalytics{ + Period: Period{ + Start: req.StartDate, + End: req.EndDate, + }, + Metrics: make(map[string]*Metric), + } + + // Revenue metrics + revenue, err := s.paymentRepo.GetRevenue(ctx, req.StartDate, req.EndDate) + if err != nil { + return nil, err + } + analytics.Metrics["total_revenue"] = &Metric{ + Value: revenue.Total.ToFloat64(), + Currency: revenue.Currency, + } + + // Success rate + successRate, err := s.paymentRepo.GetSuccessRate(ctx, req.StartDate, req.EndDate) + if err != nil { + return nil, err + } + analytics.Metrics["success_rate"] = &Metric{ + Value: successRate, + Unit: "percentage", + } + + // Gateway performance + gatewayStats, err := s.paymentRepo.GetGatewayStats(ctx, req.StartDate, req.EndDate) + if err != nil { + return nil, err + } + analytics.GatewayPerformance = gatewayStats + + // Cache results + if data, err := json.Marshal(analytics); err == nil { + s.cache.Set(ctx, cacheKey, data, 15*time.Minute) + } + + return analytics, nil +} +``` + +--- + +## ๐Ÿš€ Quick Start Implementation + +### **1. Set up the basic project structure:** + +```bash +# Create directories +mkdir -p internal/payment/{adapters,gateway,manager} +mkdir -p internal/service +mkdir -p internal/webhook +mkdir -p internal/api/v1 + +# Initialize Go modules +go mod init github.com/omkar273/codegeeky +go mod tidy +``` + +### **2. Update your service factory:** + +```go +// File: internal/service/factory.go +package service + +func NewServiceFactory( + entClient *ent.Client, + logger *logger.Logger, + cfg *config.Configuration, +) *ServiceFactory { + // Initialize payment gateway manager + gatewayManager := payment.NewGatewayManager(logger) + + // Register gateways + if cfg.Payment.Stripe.Enabled { + stripeGateway := adapters.NewStripeAdapter(cfg.Payment.Stripe) + gatewayManager.RegisterGateway("stripe", stripeGateway) + } + + if cfg.Payment.Razorpay.Enabled { + razorpayGateway := adapters.NewRazorpayAdapter(cfg.Payment.Razorpay) + gatewayManager.RegisterGateway("razorpay", razorpayGateway) + } + + return &ServiceFactory{ + EnrollmentService: NewEnrollmentService( + gatewayManager, + repository.NewEnrollmentRepository(entClient), + NewDiscountService(repository.NewDiscountRepository(entClient)), + NewNotificationService(cfg.Notification), + logger, + ), + PaymentService: NewPaymentService(gatewayManager, logger), + AnalyticsService: NewAnalyticsService( + repository.NewPaymentRepository(entClient), + cache.NewRedisCache(cfg.Redis), + logger, + ), + } +} +``` + +### **3. Add the API routes:** + +```go +// File: internal/api/router.go +func NewRouter(services *service.ServiceFactory, cfg *config.Configuration, logger *logger.Logger) *gin.Engine { + router := gin.New() + + // Middleware + router.Use(middleware.CORS()) + router.Use(middleware.RequestLogger(logger)) + router.Use(middleware.RateLimiter(cfg.RateLimit)) + + // API v1 routes + v1 := router.Group("/api/v1") + { + // Enrollment routes + enrollments := v1.Group("/enrollments") + enrollments.Use(middleware.AuthRequired()) + { + enrollments.POST("", handlers.CreateEnrollment) + enrollments.GET("/user/:user_id", handlers.GetUserEnrollments) + } + + // Payment routes + payments := v1.Group("/payments") + { + payments.POST("/webhooks/:gateway", handlers.ProcessWebhook) + payments.GET("/analytics", middleware.AdminRequired(), handlers.GetPaymentAnalytics) + } + + // Discount routes + discounts := v1.Group("/discounts") + discounts.Use(middleware.AuthRequired()) + { + discounts.POST("/validate", handlers.ValidateDiscount) + discounts.POST("/apply", handlers.ApplyDiscount) + } + } + + return router +} +``` + +This implementation provides a solid foundation for your internship enrollment workflow with comprehensive payment integration. The system is designed to be flexible, scalable, and maintainable while supporting multiple payment gateways and methods. diff --git a/docs/prds/PAYMENT_SYSTEM_IMPLEMENTATION_ROADMAP.md b/docs/prds/PAYMENT_SYSTEM_IMPLEMENTATION_ROADMAP.md new file mode 100644 index 0000000..0f9f5fe --- /dev/null +++ b/docs/prds/PAYMENT_SYSTEM_IMPLEMENTATION_ROADMAP.md @@ -0,0 +1,1418 @@ +# ๐Ÿ—บ๏ธ Payment System Implementation Roadmap + +## ๐Ÿ“‹ Overview + +This document provides a detailed implementation roadmap for the comprehensive internship enrollment workflow with payment integration system. The roadmap is structured in phases with specific milestones, deliverables, and timelines. + +--- + +## ๐Ÿš€ Implementation Phases + +### **Phase 1: Foundation & Core Architecture (Weeks 1-4)** + +#### **Week 1: Project Setup & Architecture Design** + +**Objectives:** + +- Set up development environment and project structure +- Design system architecture and database schema +- Set up CI/CD pipeline and monitoring + +**Deliverables:** + +- [ ] Project repository setup with Clean Architecture structure +- [ ] Database schema design and migrations +- [ ] Docker development environment +- [ ] CI/CD pipeline configuration +- [ ] Monitoring and logging setup + +**Technical Tasks:** + +```bash +# Project structure +mkdir -p internal/{payment,enrollment,billing,notification,webhook} +mkdir -p internal/payment/{adapters,factory,manager} +mkdir -p internal/domain/{payment,enrollment,discount} +mkdir -p cmd/{server,migrate} +mkdir -p docs/{api,architecture} +``` + +**Database Schema Implementation:** + +```sql +-- Core payment tables +CREATE TABLE payment_gateways ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(50) UNIQUE NOT NULL, + type VARCHAR(50) NOT NULL, + status gateway_status DEFAULT 'active', + config JSONB NOT NULL, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +CREATE TABLE payment_methods ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + gateway_id UUID REFERENCES payment_gateways(id), + method_type VARCHAR(50) NOT NULL, + method_name VARCHAR(100) NOT NULL, + is_active BOOLEAN DEFAULT true, + config JSONB, + created_at TIMESTAMP DEFAULT NOW() +); +``` + +#### **Week 2: Payment Gateway Interface & Core Adapters** + +**Objectives:** + +- Implement payment gateway interface and core adapters +- Set up gateway factory pattern +- Implement basic payment processing flow + +**Deliverables:** + +- [ ] Payment gateway interface definition +- [ ] Stripe gateway adapter +- [ ] Razorpay gateway adapter +- [ ] Gateway factory implementation +- [ ] Basic payment processing service + +**Code Implementation:** + +```go +// Payment Gateway Interface +type PaymentGateway interface { + Name() string + ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) + ProcessRefund(ctx context.Context, req *RefundRequest) (*RefundResponse, error) + ValidateWebhook(payload []byte, signature string) (*WebhookEvent, error) + GetSupportedMethods() []PaymentMethodType +} + +// Gateway Manager +type GatewayManager struct { + gateways map[string]PaymentGateway + selector GatewaySelector + monitor HealthMonitor +} + +func (m *GatewayManager) ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) { + gateway, err := m.selector.SelectGateway(req) + if err != nil { + return nil, fmt.Errorf("gateway selection failed: %w", err) + } + + return gateway.ProcessPayment(ctx, req) +} +``` + +#### **Week 3: Enrollment Service & Payment Integration** + +**Objectives:** + +- Implement enrollment service +- Integrate payment processing with enrollments +- Set up webhook handling system + +**Deliverables:** + +- [ ] Enrollment service implementation +- [ ] Payment-enrollment integration +- [ ] Webhook processing system +- [ ] Transaction management +- [ ] Event publishing system + +**Service Implementation:** + +```go +type EnrollmentService struct { + paymentManager *payment.GatewayManager + enrollRepo enrollment.Repository + eventPublisher events.Publisher + logger logger.Logger +} + +func (s *EnrollmentService) ProcessEnrollment(ctx context.Context, req *EnrollmentRequest) (*EnrollmentResponse, error) { + // Create enrollment record + enrollment, err := s.enrollRepo.Create(ctx, &enrollment.CreateRequest{ + UserID: req.UserID, + InternshipID: req.InternshipID, + Status: enrollment.StatusPending, + }) + if err != nil { + return nil, fmt.Errorf("failed to create enrollment: %w", err) + } + + // Process payment + paymentReq := &payment.PaymentRequest{ + Amount: req.Amount, + Currency: req.Currency, + UserID: req.UserID, + Description: fmt.Sprintf("Enrollment for internship %s", req.InternshipID), + Metadata: map[string]string{ + "enrollment_id": enrollment.ID, + "type": "enrollment", + }, + } + + paymentResp, err := s.paymentManager.ProcessPayment(ctx, paymentReq) + if err != nil { + // Update enrollment status to failed + s.enrollRepo.UpdateStatus(ctx, enrollment.ID, enrollment.StatusFailed) + return nil, fmt.Errorf("payment processing failed: %w", err) + } + + // Update enrollment with payment info + enrollment.PaymentID = &paymentResp.TransactionID + enrollment.Status = enrollment.StatusPaid + + if err := s.enrollRepo.Update(ctx, enrollment); err != nil { + return nil, fmt.Errorf("failed to update enrollment: %w", err) + } + + // Publish enrollment event + s.eventPublisher.Publish(ctx, &events.EnrollmentCompleted{ + EnrollmentID: enrollment.ID, + UserID: req.UserID, + Amount: req.Amount, + }) + + return &EnrollmentResponse{ + EnrollmentID: enrollment.ID, + PaymentID: paymentResp.TransactionID, + Status: string(enrollment.Status), + RedirectURL: paymentResp.RedirectURL, + }, nil +} +``` + +#### **Week 4: API Layer & Testing** + +**Objectives:** + +- Implement REST API endpoints +- Set up comprehensive testing +- API documentation + +**Deliverables:** + +- [ ] REST API endpoints for enrollment +- [ ] API middleware (auth, validation, rate limiting) +- [ ] Unit tests for all services +- [ ] Integration tests +- [ ] API documentation + +**API Implementation:** + +```go +type EnrollmentHandler struct { + enrollmentSvc *service.EnrollmentService + validator *validator.Validator + logger logger.Logger +} + +// POST /api/v1/enrollments +func (h *EnrollmentHandler) CreateEnrollment(c *gin.Context) { + var req dto.CreateEnrollmentRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, dto.ErrorResponse{ + Error: "invalid_request", + Message: err.Error(), + }) + return + } + + // Validate request + if err := h.validator.Validate(&req); err != nil { + c.JSON(http.StatusBadRequest, dto.ErrorResponse{ + Error: "validation_failed", + Message: err.Error(), + }) + return + } + + // Process enrollment + resp, err := h.enrollmentSvc.ProcessEnrollment(c.Request.Context(), &service.EnrollmentRequest{ + UserID: c.GetString("user_id"), + InternshipID: req.InternshipID, + Amount: req.Amount, + Currency: req.Currency, + PaymentMethod: req.PaymentMethod, + }) + if err != nil { + h.logger.Error("enrollment processing failed", "error", err) + c.JSON(http.StatusInternalServerError, dto.ErrorResponse{ + Error: "enrollment_failed", + Message: "Failed to process enrollment", + }) + return + } + + c.JSON(http.StatusCreated, resp) +} +``` + +--- + +### **Phase 2: Advanced Payment Features (Weeks 5-8)** + +#### **Week 5: Discount & Coupon System** + +**Objectives:** + +- Implement comprehensive discount system +- Add coupon management +- Integrate discounts with payment flow + +**Deliverables:** + +- [ ] Discount service implementation +- [ ] Coupon management API +- [ ] Discount calculation engine +- [ ] Admin interface for discount management +- [ ] Bulk discount support + +**Discount System:** + +```go +type DiscountService struct { + discountRepo discount.Repository + calculator *discount.Calculator + validator *discount.Validator +} + +type DiscountCalculator struct { + rules []DiscountRule +} + +func (c *DiscountCalculator) CalculateDiscount( + ctx context.Context, + originalAmount decimal.Decimal, + coupons []string, + userID string, +) (*DiscountResult, error) { + result := &DiscountResult{ + OriginalAmount: originalAmount, + FinalAmount: originalAmount, + AppliedDiscounts: make([]*AppliedDiscount, 0), + } + + for _, couponCode := range coupons { + discount, err := c.discountRepo.GetByCouponCode(ctx, couponCode) + if err != nil { + continue // Skip invalid coupons + } + + if !c.validator.IsValidForUser(discount, userID) { + continue + } + + applied := c.applyDiscount(result.FinalAmount, discount) + if applied.Amount.GreaterThan(decimal.Zero) { + result.AppliedDiscounts = append(result.AppliedDiscounts, applied) + result.FinalAmount = result.FinalAmount.Sub(applied.Amount) + result.TotalDiscount = result.TotalDiscount.Add(applied.Amount) + } + } + + return result, nil +} +``` + +#### **Week 6: Multiple Payment Methods** + +**Objectives:** + +- Expand payment method support +- Implement UPI, wallets, and bank transfers +- Add gift card system + +**Deliverables:** + +- [ ] UPI payment integration +- [ ] Digital wallet support (PayPal, Apple Pay, Google Pay) +- [ ] Bank transfer support +- [ ] Gift card system +- [ ] Offline payment tracking + +**Payment Method Expansion:** + +```go +// UPI Payment Adapter +type UPIPaymentAdapter struct { + client *upi.Client + config *UPIConfig +} + +func (a *UPIPaymentAdapter) ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) { + upiReq := &upi.PaymentRequest{ + Amount: req.Amount, + Currency: req.Currency, + VPA: req.PaymentDetails["vpa"].(string), + Description: req.Description, + OrderID: req.OrderID, + } + + resp, err := a.client.CreatePaymentRequest(ctx, upiReq) + if err != nil { + return nil, fmt.Errorf("UPI payment failed: %w", err) + } + + return &PaymentResponse{ + TransactionID: resp.TransactionID, + Status: PaymentStatusPending, + RedirectURL: resp.PaymentURL, + PaymentMethod: PaymentMethodUPI, + }, nil +} + +// Gift Card System +type GiftCardService struct { + cardRepo giftcard.Repository + paymentRepo payment.Repository + generator *giftcard.CodeGenerator +} + +func (s *GiftCardService) ApplyGiftCard(ctx context.Context, req *ApplyGiftCardRequest) (*GiftCardApplication, error) { + card, err := s.cardRepo.GetByCode(ctx, req.Code) + if err != nil { + return nil, fmt.Errorf("invalid gift card: %w", err) + } + + if card.Status != giftcard.StatusActive { + return nil, fmt.Errorf("gift card is not active") + } + + if card.Balance.LessThan(req.Amount) { + return nil, fmt.Errorf("insufficient gift card balance") + } + + // Apply gift card + newBalance := card.Balance.Sub(req.Amount) + if err := s.cardRepo.UpdateBalance(ctx, card.ID, newBalance); err != nil { + return nil, fmt.Errorf("failed to update gift card balance: %w", err) + } + + return &GiftCardApplication{ + CardID: card.ID, + AmountApplied: req.Amount, + RemainingBalance: newBalance, + }, nil +} +``` + +#### **Week 7: Subscription Management** + +**Objectives:** + +- Implement subscription billing +- Add recurring payment support +- Create subscription lifecycle management + +**Deliverables:** + +- [ ] Subscription service +- [ ] Recurring billing system +- [ ] Subscription upgrade/downgrade +- [ ] Dunning management +- [ ] Subscription analytics + +#### **Week 8: Refund & Dispute System** + +**Objectives:** + +- Implement comprehensive refund system +- Add dispute management +- Create automatic refund policies + +**Deliverables:** + +- [ ] Refund processing service +- [ ] Dispute management system +- [ ] Automatic refund rules +- [ ] Chargeback handling +- [ ] Reconciliation system + +--- + +### **Phase 3: Enterprise Features (Weeks 9-12)** + +#### **Week 9: Invoicing & Billing** + +**Objectives:** + +- Implement invoice generation +- Add tax calculation +- Create billing automation + +**Deliverables:** + +- [ ] Invoice generation service +- [ ] Tax calculation engine +- [ ] PDF invoice templates +- [ ] Automated billing cycles +- [ ] Multi-currency invoicing + +#### **Week 10: Advanced Analytics & Reporting** + +**Objectives:** + +- Create comprehensive reporting system +- Add real-time analytics +- Implement business intelligence features + +**Deliverables:** + +- [ ] Payment analytics dashboard +- [ ] Financial reporting system +- [ ] Real-time monitoring +- [ ] Business intelligence queries +- [ ] Export functionality + +#### **Week 11: Notification & Communication** + +**Objectives:** + +- Implement multi-channel notifications +- Add email template system +- Create SMS and push notification support + +**Deliverables:** + +- [ ] Notification service +- [ ] Email template engine +- [ ] SMS integration +- [ ] Push notification system +- [ ] Notification preferences + +#### **Week 12: Webhook & Integration System** + +**Objectives:** + +- Create comprehensive webhook system +- Add third-party integrations +- Implement event-driven architecture + +**Deliverables:** + +- [ ] Webhook management system +- [ ] Event processing pipeline +- [ ] Third-party integrations +- [ ] API gateway enhancements +- [ ] Integration testing + +--- + +### **Phase 4: Optimization & Launch (Weeks 13-16)** + +#### **Week 13: Performance Optimization** + +**Objectives:** + +- Optimize system performance +- Add caching layers +- Implement load balancing + +**Deliverables:** + +- [ ] Database query optimization +- [ ] Redis caching implementation +- [ ] Connection pooling +- [ ] Load balancer configuration +- [ ] Performance monitoring + +#### **Week 14: Security & Compliance** + +**Objectives:** + +- Ensure PCI DSS compliance +- Implement security best practices +- Add fraud detection + +**Deliverables:** + +- [ ] Security audit +- [ ] PCI DSS compliance verification +- [ ] Fraud detection system +- [ ] Security monitoring +- [ ] Compliance documentation + +#### **Week 15: Testing & Quality Assurance** + +**Objectives:** + +- Comprehensive testing +- Load testing +- Security testing + +**Deliverables:** + +- [ ] End-to-end testing +- [ ] Load testing results +- [ ] Security penetration testing +- [ ] User acceptance testing +- [ ] Bug fixes and optimizations + +#### **Week 16: Production Deployment** + +**Objectives:** + +- Deploy to production +- Monitor system health +- Handle post-launch issues + +**Deliverables:** + +- [ ] Production deployment +- [ ] Monitoring setup +- [ ] Alerting configuration +- [ ] Documentation completion +- [ ] Team training + +--- + +## ๐Ÿ› ๏ธ Technical Implementation Details + +### **Design Patterns to Implement** + +#### **1. Strategy Pattern - Payment Gateway Selection** + +```go +type PaymentStrategy interface { + ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) + GetName() string + GetSupportedMethods() []PaymentMethod +} + +type PaymentContext struct { + strategy PaymentStrategy +} + +func (c *PaymentContext) SetStrategy(strategy PaymentStrategy) { + c.strategy = strategy +} + +func (c *PaymentContext) ExecutePayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) { + return c.strategy.ProcessPayment(ctx, req) +} +``` + +#### **2. Factory Pattern - Gateway Creation** + +```go +type GatewayFactory interface { + CreateGateway(gatewayType string, config GatewayConfig) (PaymentGateway, error) +} + +type ConcreteGatewayFactory struct { + configManager *config.Manager +} + +func (f *ConcreteGatewayFactory) CreateGateway(gatewayType string, config GatewayConfig) (PaymentGateway, error) { + switch gatewayType { + case "stripe": + return NewStripeGateway(config.StripeConfig) + case "razorpay": + return NewRazorpayGateway(config.RazorpayConfig) + case "paypal": + return NewPayPalGateway(config.PayPalConfig) + default: + return nil, fmt.Errorf("unsupported gateway type: %s", gatewayType) + } +} +``` + +#### **3. Observer Pattern - Event Handling** + +```go +type PaymentEventObserver interface { + OnPaymentSuccess(event *PaymentSuccessEvent) + OnPaymentFailure(event *PaymentFailureEvent) + OnRefundProcessed(event *RefundEvent) +} + +type PaymentEventPublisher struct { + observers []PaymentEventObserver + mu sync.RWMutex +} + +func (p *PaymentEventPublisher) Subscribe(observer PaymentEventObserver) { + p.mu.Lock() + defer p.mu.Unlock() + p.observers = append(p.observers, observer) +} + +func (p *PaymentEventPublisher) NotifyPaymentSuccess(event *PaymentSuccessEvent) { + p.mu.RLock() + defer p.mu.RUnlock() + for _, observer := range p.observers { + go observer.OnPaymentSuccess(event) + } +} +``` + +#### **4. Decorator Pattern - Payment Enhancement** + +```go +type PaymentProcessor interface { + ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) +} + +type PaymentProcessorDecorator struct { + processor PaymentProcessor +} + +type LoggingDecorator struct { + PaymentProcessorDecorator + logger logger.Logger +} + +func (d *LoggingDecorator) ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) { + start := time.Now() + d.logger.Info("Processing payment", + "amount", req.Amount, + "method", req.PaymentMethod, + "user_id", req.UserID) + + resp, err := d.processor.ProcessPayment(ctx, req) + + duration := time.Since(start) + if err != nil { + d.logger.Error("Payment processing failed", + "error", err, + "duration", duration) + } else { + d.logger.Info("Payment processed successfully", + "transaction_id", resp.TransactionID, + "duration", duration) + } + + return resp, err +} + +type MetricsDecorator struct { + PaymentProcessorDecorator + metrics metrics.Collector +} + +func (d *MetricsDecorator) ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) { + start := time.Now() + + resp, err := d.processor.ProcessPayment(ctx, req) + + duration := time.Since(start) + d.metrics.RecordPaymentProcessingTime(duration) + + if err != nil { + d.metrics.IncrementPaymentFailures(req.PaymentMethod) + } else { + d.metrics.IncrementPaymentSuccesses(req.PaymentMethod) + } + + return resp, err +} +``` + +### **Database Schema Design** + +#### **Core Tables** + +```sql +-- Payment Gateways Configuration +CREATE TABLE payment_gateways ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(50) UNIQUE NOT NULL, + type VARCHAR(50) NOT NULL, + status gateway_status DEFAULT 'active', + priority INTEGER DEFAULT 0, + config JSONB NOT NULL, + health_check_url VARCHAR(255), + last_health_check TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Payment Methods +CREATE TABLE payment_methods ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + gateway_id UUID REFERENCES payment_gateways(id), + method_type payment_method_type NOT NULL, + method_name VARCHAR(100) NOT NULL, + is_active BOOLEAN DEFAULT true, + min_amount DECIMAL(10,2), + max_amount DECIMAL(10,2), + supported_currencies TEXT[], + config JSONB, + created_at TIMESTAMP DEFAULT NOW() +); + +-- Enrollments +CREATE TABLE enrollments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + internship_id UUID NOT NULL REFERENCES internships(id), + status enrollment_status NOT NULL DEFAULT 'pending', + payment_id UUID REFERENCES payments(id), + discount_amount DECIMAL(10,2) DEFAULT 0, + final_amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + enrolled_at TIMESTAMP, + expires_at TIMESTAMP, + metadata JSONB, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + UNIQUE(user_id, internship_id) +); + +-- Payments +CREATE TABLE payments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + enrollment_id UUID REFERENCES enrollments(id), + amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + status payment_status NOT NULL DEFAULT 'pending', + gateway_id UUID NOT NULL REFERENCES payment_gateways(id), + gateway_transaction_id VARCHAR(255), + payment_method payment_method_type, + payment_details JSONB, + failure_reason TEXT, + retry_count INTEGER DEFAULT 0, + expires_at TIMESTAMP, + processed_at TIMESTAMP, + metadata JSONB, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Payment Attempts (for retry logic) +CREATE TABLE payment_attempts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + payment_id UUID NOT NULL REFERENCES payments(id), + gateway_id UUID NOT NULL REFERENCES payment_gateways(id), + attempt_number INTEGER NOT NULL, + status payment_status NOT NULL, + gateway_response JSONB, + error_message TEXT, + processing_time_ms INTEGER, + processed_at TIMESTAMP DEFAULT NOW(), + INDEX idx_payment_attempts_payment_id (payment_id), + INDEX idx_payment_attempts_status (status) +); + +-- Discounts and Coupons +CREATE TABLE discounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + code VARCHAR(50) UNIQUE NOT NULL, + name VARCHAR(100) NOT NULL, + description TEXT, + type discount_type NOT NULL, + value DECIMAL(10,2) NOT NULL, + currency VARCHAR(3), + min_order_amount DECIMAL(10,2), + max_discount_amount DECIMAL(10,2), + usage_limit INTEGER, + usage_limit_per_user INTEGER DEFAULT 1, + used_count INTEGER DEFAULT 0, + valid_from TIMESTAMP NOT NULL, + valid_until TIMESTAMP NOT NULL, + applicable_to JSONB, -- {courses: [], categories: [], users: []} + stackable BOOLEAN DEFAULT false, + auto_apply BOOLEAN DEFAULT false, + created_by UUID REFERENCES users(id), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + INDEX idx_discounts_code (code), + INDEX idx_discounts_valid_period (valid_from, valid_until), + INDEX idx_discounts_auto_apply (auto_apply) +); + +-- Discount Usage Tracking +CREATE TABLE discount_usages ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + discount_id UUID NOT NULL REFERENCES discounts(id), + user_id UUID NOT NULL REFERENCES users(id), + payment_id UUID NOT NULL REFERENCES payments(id), + discount_amount DECIMAL(10,2) NOT NULL, + used_at TIMESTAMP DEFAULT NOW(), + UNIQUE(discount_id, payment_id) +); + +-- Refunds +CREATE TABLE refunds ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + payment_id UUID NOT NULL REFERENCES payments(id), + amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL, + reason refund_reason NOT NULL, + reason_description TEXT, + status refund_status NOT NULL DEFAULT 'pending', + gateway_refund_id VARCHAR(255), + gateway_response JSONB, + initiated_by UUID REFERENCES users(id), + processed_at TIMESTAMP, + expires_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + INDEX idx_refunds_payment_id (payment_id), + INDEX idx_refunds_status (status) +); + +-- Subscriptions +CREATE TABLE subscriptions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + plan_id UUID NOT NULL REFERENCES subscription_plans(id), + status subscription_status NOT NULL DEFAULT 'active', + billing_cycle billing_cycle NOT NULL, + amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + current_period_start TIMESTAMP NOT NULL, + current_period_end TIMESTAMP NOT NULL, + trial_end TIMESTAMP, + cancel_at_period_end BOOLEAN DEFAULT FALSE, + canceled_at TIMESTAMP, + gateway_id UUID NOT NULL REFERENCES payment_gateways(id), + gateway_subscription_id VARCHAR(255), + gateway_customer_id VARCHAR(255), + next_billing_date TIMESTAMP, + failure_count INTEGER DEFAULT 0, + last_payment_attempt TIMESTAMP, + metadata JSONB, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + INDEX idx_subscriptions_user_id (user_id), + INDEX idx_subscriptions_status (status), + INDEX idx_subscriptions_next_billing (next_billing_date) +); + +-- Subscription Plans +CREATE TABLE subscription_plans ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(100) NOT NULL, + description TEXT, + amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + billing_cycle billing_cycle NOT NULL, + trial_days INTEGER DEFAULT 0, + max_enrollments INTEGER, -- NULL for unlimited + features JSONB, + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Invoices +CREATE TABLE invoices ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + invoice_number VARCHAR(50) UNIQUE NOT NULL, + user_id UUID NOT NULL REFERENCES users(id), + subscription_id UUID REFERENCES subscriptions(id), + payment_id UUID REFERENCES payments(id), + subtotal DECIMAL(10,2) NOT NULL, + tax_amount DECIMAL(10,2) DEFAULT 0, + discount_amount DECIMAL(10,2) DEFAULT 0, + total_amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL, + status invoice_status NOT NULL DEFAULT 'draft', + due_date TIMESTAMP, + paid_at TIMESTAMP, + billing_address JSONB, + line_items JSONB NOT NULL, + tax_details JSONB, + metadata JSONB, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + INDEX idx_invoices_user_id (user_id), + INDEX idx_invoices_status (status), + INDEX idx_invoices_due_date (due_date) +); + +-- Gift Cards +CREATE TABLE gift_cards ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + code VARCHAR(50) UNIQUE NOT NULL, + initial_amount DECIMAL(10,2) NOT NULL, + current_balance DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + status gift_card_status NOT NULL DEFAULT 'active', + issued_to_user_id UUID REFERENCES users(id), + issued_by_user_id UUID REFERENCES users(id), + expires_at TIMESTAMP, + last_used_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + INDEX idx_gift_cards_code (code), + INDEX idx_gift_cards_status (status) +); + +-- Gift Card Transactions +CREATE TABLE gift_card_transactions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + gift_card_id UUID NOT NULL REFERENCES gift_cards(id), + payment_id UUID REFERENCES payments(id), + transaction_type gift_card_transaction_type NOT NULL, + amount DECIMAL(10,2) NOT NULL, + balance_before DECIMAL(10,2) NOT NULL, + balance_after DECIMAL(10,2) NOT NULL, + description TEXT, + created_at TIMESTAMP DEFAULT NOW() +); + +-- Webhooks +CREATE TABLE webhooks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + event_type VARCHAR(100) NOT NULL, + gateway_id UUID REFERENCES payment_gateways(id), + payload JSONB NOT NULL, + signature VARCHAR(255), + status webhook_status NOT NULL DEFAULT 'pending', + retry_count INTEGER DEFAULT 0, + last_retry_at TIMESTAMP, + processed_at TIMESTAMP, + error_message TEXT, + created_at TIMESTAMP DEFAULT NOW(), + INDEX idx_webhooks_status (status), + INDEX idx_webhooks_event_type (event_type), + INDEX idx_webhooks_created_at (created_at) +); + +-- Webhook Endpoints (for outgoing webhooks) +CREATE TABLE webhook_endpoints ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + url VARCHAR(500) NOT NULL, + secret VARCHAR(255) NOT NULL, + events TEXT[] NOT NULL, + is_active BOOLEAN DEFAULT true, + created_by UUID REFERENCES users(id), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Audit Logs +CREATE TABLE payment_audit_logs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + entity_type VARCHAR(50) NOT NULL, + entity_id UUID NOT NULL, + action VARCHAR(50) NOT NULL, + old_values JSONB, + new_values JSONB, + performed_by UUID REFERENCES users(id), + ip_address INET, + user_agent TEXT, + created_at TIMESTAMP DEFAULT NOW(), + INDEX idx_audit_logs_entity (entity_type, entity_id), + INDEX idx_audit_logs_created_at (created_at) +); +``` + +#### **Enums** + +```sql +-- Enrollment statuses +CREATE TYPE enrollment_status AS ENUM ( + 'pending', + 'payment_pending', + 'enrolled', + 'cancelled', + 'expired', + 'failed', + 'refunded' +); + +-- Payment statuses +CREATE TYPE payment_status AS ENUM ( + 'pending', + 'processing', + 'succeeded', + 'failed', + 'cancelled', + 'expired', + 'refunded', + 'partially_refunded' +); + +-- Payment method types +CREATE TYPE payment_method_type AS ENUM ( + 'credit_card', + 'debit_card', + 'upi', + 'net_banking', + 'wallet', + 'bank_transfer', + 'gift_card', + 'cryptocurrency', + 'offline' +); + +-- Gateway statuses +CREATE TYPE gateway_status AS ENUM ( + 'active', + 'inactive', + 'maintenance', + 'deprecated' +); + +-- Discount types +CREATE TYPE discount_type AS ENUM ( + 'percentage', + 'fixed_amount', + 'free_shipping', + 'bogo', + 'tiered' +); + +-- Refund reasons +CREATE TYPE refund_reason AS ENUM ( + 'customer_request', + 'duplicate_payment', + 'fraudulent_transaction', + 'service_not_delivered', + 'technical_error', + 'policy_violation', + 'other' +); + +-- Refund statuses +CREATE TYPE refund_status AS ENUM ( + 'pending', + 'processing', + 'succeeded', + 'failed', + 'cancelled' +); + +-- Subscription statuses +CREATE TYPE subscription_status AS ENUM ( + 'active', + 'cancelled', + 'expired', + 'past_due', + 'unpaid', + 'trialing' +); + +-- Billing cycles +CREATE TYPE billing_cycle AS ENUM ( + 'weekly', + 'monthly', + 'quarterly', + 'yearly' +); + +-- Invoice statuses +CREATE TYPE invoice_status AS ENUM ( + 'draft', + 'sent', + 'paid', + 'overdue', + 'cancelled', + 'refunded' +); + +-- Gift card statuses +CREATE TYPE gift_card_status AS ENUM ( + 'active', + 'expired', + 'used', + 'cancelled' +); + +-- Gift card transaction types +CREATE TYPE gift_card_transaction_type AS ENUM ( + 'issued', + 'used', + 'refunded', + 'expired' +); + +-- Webhook statuses +CREATE TYPE webhook_status AS ENUM ( + 'pending', + 'processing', + 'succeeded', + 'failed', + 'expired' +); +``` + +--- + +## ๐Ÿ“Š Monitoring & Metrics + +### **Key Performance Indicators (KPIs)** + +#### **Technical Metrics** + +- **Payment Success Rate**: Target > 95% +- **API Response Time**: 95th percentile < 500ms +- **Database Query Performance**: 95th percentile < 100ms +- **System Uptime**: Target 99.9% +- **Error Rate**: < 0.1% for critical paths + +#### **Business Metrics** + +- **Conversion Rate**: % of users completing enrollment +- **Cart Abandonment Rate**: % abandoning at payment step +- **Average Order Value**: Revenue per successful transaction +- **Refund Rate**: % of payments refunded +- **Customer Lifetime Value**: Long-term revenue per user + +#### **Operational Metrics** + +- **Gateway Performance**: Success rates by gateway +- **Payment Method Adoption**: Usage distribution +- **Geographic Performance**: Success rates by region +- **Fraud Detection**: False positive/negative rates +- **Support Ticket Volume**: Payment-related issues + +### **Monitoring Implementation** + +```go +// Metrics Collection +type PaymentMetrics struct { + successCounter prometheus.CounterVec + failureCounter prometheus.CounterVec + processingTime prometheus.HistogramVec + activePayments prometheus.GaugeVec + gatewayHealth prometheus.GaugeVec +} + +func NewPaymentMetrics() *PaymentMetrics { + return &PaymentMetrics{ + successCounter: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "payments_successful_total", + Help: "Total number of successful payments", + }, + []string{"gateway", "method", "currency"}, + ), + failureCounter: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "payments_failed_total", + Help: "Total number of failed payments", + }, + []string{"gateway", "method", "reason"}, + ), + processingTime: prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "payment_processing_duration_seconds", + Help: "Time taken to process payments", + Buckets: prometheus.DefBuckets, + }, + []string{"gateway", "method"}, + ), + } +} + +func (m *PaymentMetrics) RecordSuccess(gateway, method, currency string) { + m.successCounter.WithLabelValues(gateway, method, currency).Inc() +} + +func (m *PaymentMetrics) RecordFailure(gateway, method, reason string) { + m.failureCounter.WithLabelValues(gateway, method, reason).Inc() +} + +func (m *PaymentMetrics) RecordProcessingTime(gateway, method string, duration time.Duration) { + m.processingTime.WithLabelValues(gateway, method).Observe(duration.Seconds()) +} +``` + +### **Alerting Rules** + +```yaml +# Prometheus Alerting Rules +groups: + - name: payment_system + rules: + - alert: HighPaymentFailureRate + expr: rate(payments_failed_total[5m]) / rate(payments_total[5m]) > 0.05 + for: 2m + labels: + severity: critical + annotations: + summary: "High payment failure rate detected" + description: "Payment failure rate is {{ $value }}% over the last 5 minutes" + + - alert: PaymentProcessingLatency + expr: histogram_quantile(0.95, rate(payment_processing_duration_seconds_bucket[5m])) > 5 + for: 2m + labels: + severity: warning + annotations: + summary: "High payment processing latency" + description: "95th percentile latency is {{ $value }} seconds" + + - alert: GatewayDown + expr: gateway_health_check == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "Payment gateway is down" + description: "Gateway {{ $labels.gateway }} is not responding" + + - alert: HighRefundRate + expr: rate(refunds_total[1h]) / rate(payments_successful_total[1h]) > 0.10 + for: 5m + labels: + severity: warning + annotations: + summary: "High refund rate detected" + description: "Refund rate is {{ $value }}% over the last hour" +``` + +--- + +## ๐Ÿ” Security Implementation + +### **Security Measures** + +#### **Data Protection** + +```go +// Encryption Service +type EncryptionService struct { + key []byte +} + +func (s *EncryptionService) EncryptPII(data string) (string, error) { + block, err := aes.NewCipher(s.key) + if err != nil { + return "", err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + + ciphertext := gcm.Seal(nonce, nonce, []byte(data), nil) + return base64.StdEncoding.EncodeToString(ciphertext), nil +} + +func (s *EncryptionService) DecryptPII(encryptedData string) (string, error) { + data, err := base64.StdEncoding.DecodeString(encryptedData) + if err != nil { + return "", err + } + + block, err := aes.NewCipher(s.key) + if err != nil { + return "", err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return "", fmt.Errorf("ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return "", err + } + + return string(plaintext), nil +} +``` + +#### **Fraud Detection** + +```go +// Fraud Detection Service +type FraudDetectionService struct { + rules []FraudRule + ml *MLModel +} + +type FraudAnalysis struct { + RiskScore float64 + IsBlocked bool + Reasons []string + Recommended FraudAction +} + +func (s *FraudDetectionService) AnalyzePayment(ctx context.Context, req *PaymentRequest) (*FraudAnalysis, error) { + analysis := &FraudAnalysis{ + RiskScore: 0.0, + Reasons: make([]string, 0), + } + + // Rule-based analysis + for _, rule := range s.rules { + if rule.Matches(req) { + analysis.RiskScore += rule.RiskWeight + analysis.Reasons = append(analysis.Reasons, rule.Description) + } + } + + // ML-based analysis + if s.ml != nil { + mlScore := s.ml.Predict(req) + analysis.RiskScore = (analysis.RiskScore + mlScore) / 2 + } + + // Determine action + switch { + case analysis.RiskScore > 0.8: + analysis.IsBlocked = true + analysis.Recommended = FraudActionBlock + case analysis.RiskScore > 0.5: + analysis.Recommended = FraudActionReview + default: + analysis.Recommended = FraudActionAllow + } + + return analysis, nil +} +``` + +#### **Rate Limiting** + +```go +// Rate Limiter +type RateLimiter struct { + redis *redis.Client + limits map[string]RateLimit +} + +type RateLimit struct { + Requests int + Window time.Duration +} + +func (r *RateLimiter) CheckLimit(ctx context.Context, key string, limitType string) (bool, error) { + limit, exists := r.limits[limitType] + if !exists { + return true, nil // No limit configured + } + + pipe := r.redis.Pipeline() + incr := pipe.Incr(ctx, key) + pipe.Expire(ctx, key, limit.Window) + + _, err := pipe.Exec(ctx) + if err != nil { + return false, err + } + + count := incr.Val() + return count <= int64(limit.Requests), nil +} +``` + +--- + +This comprehensive implementation roadmap provides a structured approach to building a world-class payment system for internship enrollments. The roadmap includes detailed technical specifications, code examples, database schemas, and monitoring strategies to ensure successful delivery. diff --git a/docs/prds/PROJECT_ROADMAP.md b/docs/prds/PROJECT_ROADMAP.md new file mode 100644 index 0000000..c5949ca --- /dev/null +++ b/docs/prds/PROJECT_ROADMAP.md @@ -0,0 +1,725 @@ +# ๐Ÿš€ Internship Platform - 2-Week Development Roadmap & Documentation + +**Project:** LMS for Paid Internships/Experiences +**Timeline:** 2 Weeks (14 Days) +**Team Structure:** Assuming 2-3 developers +**Technology Stack:** Go + Gin + Ent + PostgreSQL + Supabase + Razorpay + Next.js + +--- + +## ๐Ÿ“Š Current State Analysis + +### โœ… **What's Already Implemented** + +- **Backend Infrastructure**: Go + Gin + Ent ORM + PostgreSQL +- **Authentication System**: Comprehensive RBAC/ABAC with Supabase +- **Payment Integration**: Razorpay with payment schema +- **Core Database Schemas**: User, Internship, Category, Discount, Payment, PaymentAttempt, FileUpload, Enrollment +- **API Foundation**: V1 REST API with CRUD operations for internships, categories, discounts +- **Webhook System**: Event-driven architecture with pub/sub +- **Configuration Management**: Comprehensive config system with validation +- **File Upload**: Cloudinary integration +- **Security**: JWT validation, encryption, middleware + +### โŒ **What's Missing** + +- Enrollment workflow APIs +- Communication tools (chat, video calls) +- Mentorship system +- Task/project management +- Notifications system +- Review/rating system +- Admin dashboard +- Frontend application +- Mobile responsiveness +- Real-time features + +--- + +## ๐ŸŽฏ Two-Week Sprint Plan + +### **Week 1: Core Feature Development** + +#### **Day 1-2: Enrollment System & Application Workflow** + +**Priority: CRITICAL** + +**Tasks:** + +1. **Enrollment API Implementation** + + - Create enrollment service layer + - Implement enrollment repository + - Add enrollment endpoints to API router + - Implement application status tracking + +2. **Payment Integration with Enrollment** + - Link payments to enrollments + - Handle payment success/failure webhooks + - Implement enrollment status updates based on payment + +**Deliverables:** + +- `/v1/enrollments` CRUD endpoints +- `/v1/internships/{id}/apply` endpoint +- Payment flow integration +- Status tracking system + +--- + +#### **Day 3-4: User Experience Enhancement** + +**Priority: HIGH** + +**Tasks:** + +1. **Application Status Visibility** + + - Endpoint to view application status + - Peer visibility features + - Application history tracking + +2. **Enhanced User Profiles** + - Resume/portfolio upload + - Skill management + - Profile completion tracking + +**Deliverables:** + +- `/v1/user/applications` endpoint +- `/v1/user/profile/documents` endpoint +- Enhanced user profile APIs +- Application status dashboard data + +--- + +#### **Day 5-7: Communication & Collaboration Foundation** + +**Priority: HIGH** + +**Tasks:** + +1. **Chat System** + + - Real-time messaging with WebSockets + - Group chats for internships + - Direct messaging between users + +2. **Notification System** + - Email notifications + - In-app notifications + - Push notification infrastructure + +**Deliverables:** + +- WebSocket chat server +- Notification service +- Message storage and retrieval APIs +- Email integration + +--- + +### **Week 2: Advanced Features & Polish** + +#### **Day 8-9: Mentorship & Task Management** + +**Priority: MEDIUM** + +**Tasks:** + +1. **Mentorship System** + + - Schedule mentorship sessions + - Mentor-student matching + - Session tracking and notes + +2. **Task Management** + - Task creation and assignment + - Progress tracking + - Deadline management + +**Deliverables:** + +- Mentorship APIs +- Task management system +- Calendar integration endpoints +- Progress tracking dashboard + +--- + +#### **Day 10-11: Video Calls & Advanced Features** + +**Priority: MEDIUM** + +**Tasks:** + +1. **Video Integration** + + - Jitsi Meet integration + - Room creation and management + - Call history tracking + +2. **Review & Rating System** + - Internship reviews + - Mentor ratings + - Review moderation + +**Deliverables:** + +- Video call integration +- Review/rating APIs +- Moderation tools + +--- + +#### **Day 12-14: Admin Dashboard & Final Polish** + +**Priority: HIGH** + +**Tasks:** + +1. **Admin Dashboard Backend** + + - User management APIs + - Internship approval system + - Analytics and reporting + - Content moderation + +2. **System Optimization** + - Performance optimization + - Security audit + - API documentation + - Testing and bug fixes + +**Deliverables:** + +- Complete admin API suite +- Optimized and tested system +- Production-ready deployment + +--- + +## ๐Ÿ—๏ธ Detailed Implementation Guide + +### **1. Enrollment System Implementation** + +#### **Service Layer** (`internal/service/enrollment.go`) + +```go +type EnrollmentService interface { + Apply(ctx context.Context, userID, internshipID string, paymentData PaymentData) (*Enrollment, error) + GetUserEnrollments(ctx context.Context, userID string) ([]*Enrollment, error) + GetInternshipEnrollments(ctx context.Context, internshipID string) ([]*Enrollment, error) + UpdateEnrollmentStatus(ctx context.Context, enrollmentID string, status EnrollmentStatus) error + ProcessPaymentWebhook(ctx context.Context, paymentID string, status PaymentStatus) error + GetApplicationStatus(ctx context.Context, userID, internshipID string) (*ApplicationStatus, error) + GetPeerApplications(ctx context.Context, internshipID string) ([]*PeerApplication, error) +} +``` + +#### **API Endpoints** (`internal/api/v1/enrollment.go`) + +```go +// POST /v1/internships/{id}/apply +func (h *EnrollmentHandler) ApplyForInternship(c *gin.Context) + +// GET /v1/user/applications +func (h *EnrollmentHandler) GetUserApplications(c *gin.Context) + +// GET /v1/internships/{id}/applications +func (h *EnrollmentHandler) GetInternshipApplications(c *gin.Context) + +// GET /v1/internships/{id}/peers +func (h *EnrollmentHandler) GetPeerApplications(c *gin.Context) + +// PUT /v1/enrollments/{id}/status +func (h *EnrollmentHandler) UpdateEnrollmentStatus(c *gin.Context) +``` + +### **2. Communication System Architecture** + +#### **Chat System** (`internal/chat/`) + +```go +type ChatService interface { + CreateRoom(ctx context.Context, internshipID string) (*ChatRoom, error) + SendMessage(ctx context.Context, roomID, userID, message string) (*Message, error) + GetMessages(ctx context.Context, roomID string, pagination Pagination) ([]*Message, error) + JoinRoom(ctx context.Context, roomID, userID string) error + LeaveRoom(ctx context.Context, roomID, userID string) error +} +``` + +#### **WebSocket Handler** (`internal/websocket/`) + +```go +type Hub struct { + clients map[*Client]bool + broadcast chan []byte + register chan *Client + unregister chan *Client +} + +type Client struct { + hub *Hub + conn *websocket.Conn + send chan []byte + userID string + roomID string +} +``` + +### **3. Notification System** (`internal/notification/`) + +#### **Service Interface** + +```go +type NotificationService interface { + SendEmail(ctx context.Context, to, subject, body string) error + SendInAppNotification(ctx context.Context, userID string, notification Notification) error + SendPushNotification(ctx context.Context, userID string, notification Notification) error + GetUserNotifications(ctx context.Context, userID string) ([]*Notification, error) + MarkAsRead(ctx context.Context, notificationID string) error +} +``` + +#### **Notification Types** + +```go +const ( + NotificationTypeApplicationStatus = "application_status" + NotificationTypePaymentConfirmation = "payment_confirmation" + NotificationTypeInterviewSchedule = "interview_schedule" + NotificationTypeMentorshipSession = "mentorship_session" + NotificationTypeTaskAssignment = "task_assignment" + NotificationTypeMessage = "message" +) +``` + +### **4. Database Schema Extensions** + +#### **New Tables Required** + +```sql +-- Chat Rooms +CREATE TABLE chat_rooms ( + id VARCHAR(255) PRIMARY KEY, + internship_id VARCHAR(255) NOT NULL, + name VARCHAR(255), + type VARCHAR(50) DEFAULT 'group', + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Messages +CREATE TABLE messages ( + id VARCHAR(255) PRIMARY KEY, + room_id VARCHAR(255) NOT NULL, + user_id VARCHAR(255) NOT NULL, + content TEXT NOT NULL, + message_type VARCHAR(50) DEFAULT 'text', + created_at TIMESTAMP DEFAULT NOW(), + FOREIGN KEY (room_id) REFERENCES chat_rooms(id), + FOREIGN KEY (user_id) REFERENCES users(id) +); + +-- Notifications +CREATE TABLE notifications ( + id VARCHAR(255) PRIMARY KEY, + user_id VARCHAR(255) NOT NULL, + title VARCHAR(255) NOT NULL, + content TEXT, + type VARCHAR(50) NOT NULL, + read_at TIMESTAMP NULL, + created_at TIMESTAMP DEFAULT NOW(), + FOREIGN KEY (user_id) REFERENCES users(id) +); + +-- Mentorship Sessions +CREATE TABLE mentorship_sessions ( + id VARCHAR(255) PRIMARY KEY, + internship_id VARCHAR(255) NOT NULL, + mentor_id VARCHAR(255) NOT NULL, + student_id VARCHAR(255) NOT NULL, + scheduled_at TIMESTAMP NOT NULL, + duration_minutes INTEGER DEFAULT 30, + status VARCHAR(50) DEFAULT 'scheduled', + meeting_url VARCHAR(500), + notes TEXT, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Tasks +CREATE TABLE tasks ( + id VARCHAR(255) PRIMARY KEY, + internship_id VARCHAR(255) NOT NULL, + created_by VARCHAR(255) NOT NULL, + assigned_to VARCHAR(255) NOT NULL, + title VARCHAR(255) NOT NULL, + description TEXT, + due_date TIMESTAMP, + status VARCHAR(50) DEFAULT 'pending', + priority VARCHAR(50) DEFAULT 'medium', + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Reviews +CREATE TABLE reviews ( + id VARCHAR(255) PRIMARY KEY, + internship_id VARCHAR(255) NOT NULL, + user_id VARCHAR(255) NOT NULL, + mentor_id VARCHAR(255), + rating INTEGER CHECK (rating >= 1 AND rating <= 5), + review_text TEXT, + is_public BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT NOW(), + UNIQUE(internship_id, user_id) +); +``` + +--- + +## ๐Ÿ› ๏ธ Implementation Priorities + +### **Critical Path Features (Must Have)** + +1. **Enrollment System**: Core application flow +2. **Payment Integration**: Revenue generation +3. **Basic Communication**: User engagement +4. **Admin Dashboard**: Platform management +5. **User Profiles**: Trust and verification + +### **High Impact Features (Should Have)** + +1. **Notifications**: User retention +2. **Mentorship System**: Value proposition +3. **Task Management**: Learning outcomes +4. **Review System**: Trust building + +### **Nice to Have Features** + +1. **Video Calls**: Enhanced interaction +2. **Advanced Analytics**: Business insights +3. **Mobile App**: Accessibility +4. **AI Matching**: Improved UX + +--- + +## ๐Ÿš€ Daily Execution Plan + +### **Day 1: Enrollment System Foundation** + +```bash +# Morning (4 hours) +- Create enrollment service layer +- Implement enrollment repository +- Add enrollment entity relationships + +# Afternoon (4 hours) +- Create enrollment API endpoints +- Implement apply-for-internship flow +- Add payment integration hooks +``` + +### **Day 2: Payment & Enrollment Integration** + +```bash +# Morning (4 hours) +- Implement payment success webhook handler +- Create enrollment status update logic +- Add payment verification flow + +# Afternoon (4 hours) +- Test payment integration +- Implement refund handling +- Add enrollment status notifications +``` + +### **Day 3: User Experience Features** + +```bash +# Morning (4 hours) +- Create application status endpoints +- Implement peer visibility features +- Add application history tracking + +# Afternoon (4 hours) +- Enhance user profile endpoints +- Add document upload functionality +- Implement skill management +``` + +### **Day 4: Profile & Document Management** + +```bash +# Morning (4 hours) +- Complete profile enhancement features +- Add resume/portfolio upload +- Implement profile completion tracking + +# Afternoon (4 hours) +- Create profile validation system +- Add profile image upload +- Implement profile public/private settings +``` + +### **Day 5-6: Communication System** + +```bash +# Day 5 Morning: WebSocket Infrastructure +- Set up WebSocket server +- Create chat room management +- Implement real-time message handling + +# Day 5 Afternoon: Chat Features +- Add message persistence +- Implement chat history +- Create room management APIs + +# Day 6 Morning: Notification System +- Create notification service +- Implement email notifications +- Add in-app notification storage + +# Day 6 Afternoon: Notification Integration +- Integrate notifications with all workflows +- Add notification preferences +- Implement push notification infrastructure +``` + +### **Day 7: Communication Polish** + +```bash +# Morning (4 hours) +- Add file sharing in chat +- Implement message reactions +- Add typing indicators + +# Afternoon (4 hours) +- Test communication features +- Optimize performance +- Add rate limiting +``` + +### **Day 8-9: Mentorship & Tasks** + +```bash +# Day 8: Mentorship System +- Create mentorship session management +- Implement mentor-student matching +- Add session scheduling system + +# Day 9: Task Management +- Create task assignment system +- Implement progress tracking +- Add deadline management +``` + +### **Day 10-11: Advanced Features** + +```bash +# Day 10: Video Integration +- Integrate Jitsi Meet +- Create room management +- Add call history tracking + +# Day 11: Review System +- Implement review/rating APIs +- Add review moderation +- Create rating aggregation +``` + +### **Day 12-14: Admin & Polish** + +```bash +# Day 12: Admin Dashboard +- Create admin APIs +- Implement user management +- Add content moderation + +# Day 13: Analytics & Reporting +- Implement analytics endpoints +- Create dashboard data APIs +- Add reporting features + +# Day 14: Final Polish +- Performance optimization +- Security audit +- Documentation completion +- Production deployment preparation +``` + +--- + +## ๐Ÿ“‹ Testing Strategy + +### **Unit Testing** + +- Service layer tests (80% coverage minimum) +- Repository layer tests +- Utility function tests + +### **Integration Testing** + +- API endpoint tests +- Database integration tests +- Payment integration tests +- Webhook handling tests + +### **End-to-End Testing** + +- Complete user workflows +- Payment flows +- Communication features +- Admin operations + +--- + +## ๐Ÿ”ง DevOps & Deployment + +### **Development Environment** + +```yaml +# docker-compose.dev.yml +version: "3.8" +services: + postgres: + image: postgres:15 + environment: + POSTGRES_DB: codegeeky_dev + POSTGRES_USER: dev + POSTGRES_PASSWORD: dev + ports: + - "5432:5432" + + redis: + image: redis:7 + ports: + - "6379:6379" + + api: + build: . + ports: + - "8080:8080" + depends_on: + - postgres + - redis +``` + +### **Production Deployment** + +- **Container Orchestration**: Docker + Kubernetes or Docker Swarm +- **Database**: Managed PostgreSQL (Neon DB) +- **Cache**: Redis Cloud +- **File Storage**: Cloudinary +- **Monitoring**: Prometheus + Grafana +- **Logging**: ELK Stack or Loki + +--- + +## ๐Ÿ”’ Security Considerations + +### **Authentication & Authorization** + +- JWT token validation with Supabase +- Role-based access control (RBAC) +- Attribute-based access control (ABAC) +- Session management + +### **Data Protection** + +- Data encryption at rest and in transit +- PII data handling compliance +- Payment data security (PCI DSS) +- Regular security audits + +### **API Security** + +- Rate limiting +- Input validation +- SQL injection prevention +- XSS protection +- CORS configuration + +--- + +## ๐Ÿ“Š Success Metrics + +### **Technical Metrics** + +- API response time < 200ms (95th percentile) +- System uptime > 99.9% +- Database query performance optimization +- Memory usage < 512MB per instance + +### **Business Metrics** + +- User registration rate +- Application completion rate +- Payment success rate > 95% +- User engagement metrics +- Retention rates + +--- + +## ๐ŸŽ‰ Launch Checklist + +### **Pre-Launch (Day 13-14)** + +- [ ] All critical features tested +- [ ] Security audit completed +- [ ] Performance optimization verified +- [ ] Documentation updated +- [ ] Backup and recovery tested +- [ ] Monitoring and alerting configured +- [ ] Production environment provisioned +- [ ] SSL certificates configured +- [ ] DNS configuration completed + +### **Launch Day** + +- [ ] Deploy to production +- [ ] Verify all services running +- [ ] Test critical user flows +- [ ] Monitor system performance +- [ ] Enable monitoring alerts +- [ ] Prepare rollback plan + +### **Post-Launch (Week 3)** + +- [ ] Monitor user feedback +- [ ] Track performance metrics +- [ ] Fix any critical issues +- [ ] Plan next iteration features +- [ ] Conduct retrospective meeting + +--- + +## ๐Ÿ”„ Future Roadmap (Beyond 2 Weeks) + +### **Phase 2: Mobile App Development** + +- React Native or Flutter mobile app +- Push notifications +- Offline capabilities +- Mobile-specific features + +### **Phase 3: AI/ML Features** + +- Intelligent internship matching +- Automated mentorship recommendations +- Predictive analytics +- Content personalization + +### **Phase 4: Scale & Optimization** + +- Microservices architecture +- Advanced caching strategies +- CDN integration +- Multi-region deployment + +--- + +This roadmap provides a comprehensive guide to building a production-ready internship platform in 2 weeks. The key to success will be maintaining strict priorities, conducting daily standups, and focusing on MVP features first while ensuring code quality and security standards are maintained. diff --git a/docs/prds/SYSTEM_ARCHITECTURE.md b/docs/prds/SYSTEM_ARCHITECTURE.md index bbc2d89..7c835a2 100644 --- a/docs/prds/SYSTEM_ARCHITECTURE.md +++ b/docs/prds/SYSTEM_ARCHITECTURE.md @@ -129,12 +129,12 @@ This document provides a comprehensive overview of the event-driven pubsub syste โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 1. โ”‚โ”€โ”€โ”€ Webhook โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ โ”‚ + 1. โ”‚โ”€โ”€โ”€ Webhook โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 2. โ”‚ โ”‚โ”€ Validate โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ - โ”‚ โ”‚ Signature โ”‚ โ”‚ โ”‚ + 2. โ”‚ โ”‚โ”€ Validate โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ + โ”‚ โ”‚ Signature โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 3. โ”‚ โ”‚ โ”‚โ”€ Transform โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ + 3. โ”‚ โ”‚ โ”‚โ”€ Transform โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ โ”‚ โ”‚ to Internal โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Event Format โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ @@ -156,19 +156,19 @@ This document provides a comprehensive overview of the event-driven pubsub syste โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 1. โ”‚โ”€โ”€โ”€ Publish โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ โ”‚ - โ”‚ Event โ”‚ โ”‚ โ”‚ โ”‚ + 1. โ”‚โ”€โ”€โ”€ Publish โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ โ”‚ + โ”‚ Event โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 2. โ”‚ โ”‚โ”€โ”€โ”€ Route โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ - โ”‚ โ”‚ to RT โ”‚ โ”‚ โ”‚ - โ”‚ โ”‚ Handler โ”‚ โ”‚ โ”‚ + 2. โ”‚ โ”‚โ”€โ”€โ”€ Route โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ + โ”‚ โ”‚ to RT โ”‚ โ”‚ โ”‚ + โ”‚ โ”‚ Handler โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 3. โ”‚ โ”‚ โ”‚โ”€โ”€โ”€ Broadcast โ”€โ”€โ–ถโ”‚ โ”‚ - โ”‚ โ”‚ โ”‚ to User โ”‚ โ”‚ - โ”‚ โ”‚ โ”‚ Connection โ”‚ โ”‚ + 3. โ”‚ โ”‚ โ”‚โ”€โ”€โ”€ Broadcast โ”€โ–ถโ”‚ โ”‚ + โ”‚ โ”‚ โ”‚ to User โ”‚ โ”‚ + โ”‚ โ”‚ โ”‚ Connection โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 4. โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€โ”€ Send Event โ”€โ–ถโ”‚ - โ”‚ โ”‚ โ”‚ โ”‚ via WS โ”‚ + 4. โ”‚ โ”‚ โ”‚ โ”‚โ”€โ”€โ”€ Send Event โ–ถโ”‚ + โ”‚ โ”‚ โ”‚ โ”‚ via WS โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 5. โ”‚ โ”‚ โ”‚ โ”‚ โ”‚โ”€ Update UI โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Show Notification @@ -185,7 +185,7 @@ This document provides a comprehensive overview of the event-driven pubsub syste โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ Application โ”‚ โ”‚ Memory โ”‚ โ”‚ PostgreSQL โ”‚ โ”‚ +โ”‚ โ”‚ Application โ”‚ โ”‚ Memory โ”‚ โ”‚ PostgreSQL โ”‚ โ”‚ โ”‚ โ”‚ Server โ”‚โ”€โ”€โ”€โ”€โ”‚ PubSub โ”‚ โ”‚ Database โ”‚ โ”‚ โ”‚ โ”‚ (Port: 8080) โ”‚ โ”‚ โ”‚ โ”‚ (Port: 5432) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ @@ -374,19 +374,19 @@ Message Broker โ”‚ Application โ”‚ โ”‚ Gateway โ”‚ โ”‚ Service โ”‚ โ”‚ System โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ - 1. โ”‚โ”€โ”€โ”€ Request โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ - โ”‚ + JWT Token โ”‚ โ”‚ โ”‚ + 1. โ”‚โ”€โ”€โ”€ Request โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ + โ”‚ + JWT Token โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 2. โ”‚ โ”‚โ”€โ”€โ”€ Validate โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ โ”‚ Token โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 3. โ”‚ โ”‚โ—€โ”€โ”€โ”€ User Info โ”€โ”€โ”€โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 4. โ”‚ โ”‚โ”€โ”€โ”€ Process โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ + 4. โ”‚ โ”‚โ”€โ”€โ”€ Process โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ โ”‚ โ”‚ Event โ”‚ โ”‚ โ”‚ โ”‚ + User Contextโ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - 5. โ”‚โ—€โ”€โ”€โ”€ Response โ”€โ”€โ”€โ”€โ”‚โ—€โ”€โ”€โ”€ Success โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ + 5. โ”‚โ—€โ”€โ”€โ”€ Response โ”€โ”€โ”€โ”€โ”‚โ—€โ”€โ”€โ”€ Success โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ ``` diff --git a/docs/prds/checkout/CHECKOUT_ENROLLMENT_FLOW_PRD.md b/docs/prds/checkout/CHECKOUT_ENROLLMENT_FLOW_PRD.md new file mode 100644 index 0000000..2b368ac --- /dev/null +++ b/docs/prds/checkout/CHECKOUT_ENROLLMENT_FLOW_PRD.md @@ -0,0 +1,642 @@ +# ๐Ÿ›’ **COMPREHENSIVE CHECKOUT & ENROLLMENT FLOW PRD** + +## ๐Ÿ“‹ **Executive Summary** + +This Product Requirements Document (PRD) outlines the implementation of a production-ready e-commerce checkout and enrollment system for the Interns Go-Backend platform. The system will enable users to seamlessly purchase internships through a modern, fault-tolerant checkout flow with payment integration, discount management, and automated enrollment processing. + +### ๐ŸŽฏ **Vision Statement** + +Create a world-class checkout experience that rivals platforms like Udemy, Coursera, and Internshala, providing users with a seamless journey from internship discovery to successful enrollment, while maintaining enterprise-grade reliability, security, and scalability. + +--- + +## ๐Ÿ” **Market Research & Competitive Analysis** + +### **Platform Benchmarking** + +#### **Udemy's Checkout Flow** + +- **Cart Management**: Persistent cart with session-based items +- **Payment Methods**: Credit cards, PayPal, Apple Pay, regional methods +- **Discount Strategy**: Coupon codes, flash sales, instructor discounts +- **User Experience**: Single-page checkout with real-time validation +- **Success Rate**: 95%+ payment success rate + +#### **Coursera's Enrollment Process** + +- **Subscription Model**: Monthly/yearly plans with course access +- **Enterprise Integration**: Corporate billing and team management +- **Financial Aid**: Need-based assistance program +- **Global Reach**: Multi-currency support with local payment methods + +#### **Internshala's Approach** + +- **Internship-Specific**: Tailored for internship and training programs +- **Payment Flexibility**: Multiple payment options including EMI +- **Corporate Partnerships**: Bulk enrollment for companies +- **Regional Focus**: India-specific payment methods (UPI, Net Banking) + +### **Key Insights for Our Implementation** + +1. **Cart Persistence**: Users expect their cart to persist across sessions +2. **Payment Method Diversity**: Support for local and international payment methods +3. **Discount Transparency**: Clear display of applied discounts and savings +4. **Mobile-First Design**: Optimized for mobile checkout experience +5. **Real-Time Validation**: Immediate feedback on payment and enrollment status + +--- + +## ๐ŸŽฏ **Product Objectives** + +### **Primary Goals** + +1. **Seamless User Experience**: Reduce checkout abandonment to < 5% +2. **Payment Success Rate**: Achieve > 98% payment success rate +3. **Idempotent Operations**: Zero duplicate enrollments or charges +4. **Fault Tolerance**: 99.9% system uptime with graceful degradation +5. **Scalability**: Support 10,000+ concurrent checkout sessions + +### **Success Metrics** + +- **Conversion Rate**: > 85% cart-to-enrollment conversion +- **Payment Processing Time**: < 3 seconds average +- **Webhook Reliability**: 100% event processing with dead-letter handling +- **User Satisfaction**: > 4.5/5 rating on checkout experience +- **Technical Performance**: P95 API response time < 200ms + +--- + +## ๐Ÿ‘ฅ **User Personas** + +### **1. Individual Student (Primary)** + +- **Demographics**: 18-25 years, tech-savvy, price-conscious +- **Payment Preferences**: UPI, credit cards, digital wallets +- **Pain Points**: Complex checkout, payment failures, unclear pricing +- **Goals**: Quick, secure, transparent enrollment process + +### **2. Working Professional (Secondary)** + +- **Demographics**: 25-35 years, career-focused, time-constrained +- **Payment Preferences**: Credit cards, EMI options, corporate billing +- **Pain Points**: Limited payment options, slow processing +- **Goals**: Fast checkout with flexible payment terms + +### **3. Corporate Client (Tertiary)** + +- **Demographics**: HR managers, L&D professionals +- **Payment Preferences**: Bank transfers, invoicing, bulk payments +- **Pain Points**: Manual processes, lack of reporting +- **Goals**: Bulk enrollments with detailed tracking + +--- + +## ๐Ÿ”ง **Functional Requirements** + +### **1. Cart Management System** + +#### **Core Features** + +- **Cart Creation**: Automatic cart creation for authenticated users +- **Item Management**: Add, remove, update internship quantities +- **Cart Persistence**: Session-based cart with database backup +- **Cart Expiration**: Automatic cleanup after 24 hours of inactivity +- **Cart Sharing**: Share cart via unique URL (optional) + +#### **Technical Specifications** + +```go +type Cart struct { + ID string `json:"id"` + UserID string `json:"user_id"` + Status CartStatus `json:"status"` + Items []CartItem `json:"items"` + Subtotal decimal.Decimal `json:"subtotal"` + Total decimal.Decimal `json:"total"` + DiscountCode *string `json:"discount_code,omitempty"` + DiscountAmount decimal.Decimal `json:"discount_amount"` + ExpiresAt time.Time `json:"expires_at"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type CartItem struct { + InternshipID string `json:"internship_id"` + Title string `json:"title"` + Price decimal.Decimal `json:"price"` + Quantity int `json:"quantity"` + Metadata map[string]any `json:"metadata,omitempty"` +} +``` + +#### **API Endpoints** + +``` +POST /api/v1/cart # Create/retrieve cart +PATCH /api/v1/cart/items/:item_id # Update cart item +DELETE /api/v1/cart/items/:item_id # Remove cart item +POST /api/v1/cart/discount # Apply discount code +DELETE /api/v1/cart/discount # Remove discount +GET /api/v1/cart # Get cart details +``` + +### **2. Discount & Coupon System** + +#### **Coupon Types** + +- **Percentage Discount**: 10%, 20%, 50% off total +- **Fixed Amount**: โ‚น500, $50 off total +- **Free Enrollment**: 100% discount for specific internships +- **Bulk Discount**: Tiered pricing for multiple enrollments +- **Referral Codes**: Friend referral discounts + +#### **Validation Rules** + +- **Usage Limits**: Per user, total usage, time-based restrictions +- **Minimum Order Value**: Minimum cart value for coupon application +- **Combinability**: Multiple coupons per transaction (configurable) +- **Expiration**: Automatic expiration based on valid date range +- **Category Restrictions**: Internship-specific or category-specific coupons + +#### **API Endpoints** + +``` +POST /api/v1/cart/discount/validate # Validate discount code +POST /api/v1/cart/discount/apply # Apply discount to cart +DELETE /api/v1/cart/discount # Remove applied discount +``` + +### **3. Order Management System** + +#### **Order Lifecycle** + +1. **PENDING**: Order created, awaiting payment +2. **PAID**: Payment successful, enrollment processing +3. **FAILED**: Payment failed, order cancelled +4. **EXPIRED**: Order expired due to timeout +5. **REFUNDED**: Order refunded (future enhancement) + +#### **Order Features** + +- **Idempotency**: Prevent duplicate orders with idempotency keys +- **Payment Linking**: Direct link to payment attempts +- **Metadata Storage**: Flexible metadata for business logic +- **Audit Trail**: Complete order history and status changes + +#### **Technical Specifications** + +```go +type Order struct { + ID string `json:"id"` + UserID string `json:"user_id"` + CartID string `json:"cart_id"` + Status OrderStatus `json:"status"` + Amount decimal.Decimal `json:"amount"` + Currency string `json:"currency"` + PaymentAttemptID *string `json:"payment_attempt_id,omitempty"` + IdempotencyKey string `json:"idempotency_key"` + Metadata map[string]any `json:"metadata,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} +``` + +### **4. Payment Integration System** + +#### **Supported Gateways** + +- **Primary**: Razorpay (India-focused) +- **Secondary**: Stripe (International) +- **Future**: PayPal, Paytm, PhonePe + +#### **Payment Methods** + +- **Credit/Debit Cards**: Visa, Mastercard, American Express, RuPay +- **UPI**: All UPI-enabled apps (GPay, PhonePe, Paytm) +- **Net Banking**: Major Indian banks +- **Digital Wallets**: Paytm, PhonePe, Amazon Pay +- **EMI**: No-cost EMI options (Razorpay) + +#### **Payment Flow** + +1. **Session Creation**: Create payment session with gateway +2. **Redirect**: Redirect user to hosted checkout +3. **Payment Processing**: User completes payment on gateway +4. **Webhook Notification**: Gateway notifies success/failure +5. **Status Update**: Update order and trigger enrollment + +### **5. Webhook & Event System** + +#### **Webhook Processing** + +- **Signature Verification**: HMAC verification for security +- **Idempotency**: Prevent duplicate webhook processing +- **Retry Logic**: Exponential backoff for failed webhooks +- **Dead Letter Queue**: Handle permanently failed events + +#### **Event Publishing** + +- **Order Events**: OrderPaid, OrderFailed, OrderExpired +- **Payment Events**: PaymentSuccess, PaymentFailed +- **Enrollment Events**: EnrollmentCreated, EnrollmentFailed + +#### **Event Consumers** + +- **Enrollment Worker**: Process successful payments and create enrollments +- **Notification Worker**: Send confirmation emails and push notifications +- **Analytics Worker**: Track conversion metrics and business intelligence + +### **6. Enrollment Automation** + +#### **Enrollment Creation** + +- **Automatic Processing**: Create enrollments after successful payment +- **Idempotency**: Prevent duplicate enrollments +- **Batch Processing**: Handle multiple internships in single order +- **Status Tracking**: Track enrollment status and progress + +#### **Enrollment Features** + +- **Access Control**: Grant immediate access to internship content +- **Progress Tracking**: Initialize learning progress +- **Certificate Eligibility**: Mark user as eligible for certification +- **Support Integration**: Create support tickets if needed + +--- + +## ๐Ÿ—๏ธ **Technical Architecture** + +### **System Components** + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Frontend UI โ”‚ โ”‚ API Gateway โ”‚ โ”‚ Load Balancer โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Gin Router โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ โ”‚ โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Cart Service โ”‚ โ”‚ Order Service โ”‚ โ”‚ Payment Service โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ PostgreSQL โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ โ”‚ โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Webhook Handlerโ”‚ โ”‚ Event Consumer โ”‚ โ”‚ Payment Gateway โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### **Database Schema** + +#### **Cart Table** + +```sql +CREATE TABLE carts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + status cart_status DEFAULT 'draft', + items JSONB NOT NULL DEFAULT '[]', + subtotal DECIMAL(10,2) NOT NULL DEFAULT 0, + total DECIMAL(10,2) NOT NULL DEFAULT 0, + discount_code VARCHAR(50), + discount_amount DECIMAL(10,2) NOT NULL DEFAULT 0, + expires_at TIMESTAMP NOT NULL, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + + UNIQUE(user_id, status) +); + +CREATE INDEX idx_carts_user_id ON carts(user_id); +CREATE INDEX idx_carts_expires_at ON carts(expires_at); +``` + +#### **Order Table** + +```sql +CREATE TABLE orders ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + cart_id UUID NOT NULL REFERENCES carts(id), + status order_status DEFAULT 'pending', + amount DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'INR', + payment_attempt_id UUID REFERENCES payment_attempts(id), + idempotency_key VARCHAR(255) UNIQUE NOT NULL, + metadata JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +CREATE INDEX idx_orders_user_id ON orders(user_id); +CREATE INDEX idx_orders_status ON orders(status); +CREATE INDEX idx_orders_payment_attempt_id ON orders(payment_attempt_id); +``` + +#### **Idempotency Table** + +```sql +CREATE TABLE idempotency_keys ( + key VARCHAR(255) PRIMARY KEY, + request_hash VARCHAR(64) NOT NULL, + response_body JSONB, + status_code INTEGER, + expires_at TIMESTAMP NOT NULL, + created_at TIMESTAMP DEFAULT NOW() +); + +CREATE INDEX idx_idempotency_expires_at ON idempotency_keys(expires_at); +``` + +### **Service Dependencies** + +``` +CartService +โ”œโ”€โ”€ CartRepository +โ”œโ”€โ”€ DiscountService +โ””โ”€โ”€ PricingService + +OrderService +โ”œโ”€โ”€ OrderRepository +โ”œโ”€โ”€ CartService +โ”œโ”€โ”€ PaymentService +โ””โ”€โ”€ IdempotencyService + +CheckoutService +โ”œโ”€โ”€ OrderService +โ”œโ”€โ”€ PaymentService +โ”œโ”€โ”€ CartService +โ””โ”€โ”€ EventPublisher + +PaymentService +โ”œโ”€โ”€ PaymentRepository +โ”œโ”€โ”€ PaymentGateway +โ””โ”€โ”€ WebhookHandler + +EnrollmentWorker +โ”œโ”€โ”€ InternshipEnrollmentRepository +โ”œโ”€โ”€ OrderRepository +โ””โ”€โ”€ NotificationService +``` + +--- + +## ๐Ÿ”’ **Security Requirements** + +### **Data Protection** + +- **PCI DSS Compliance**: Secure handling of payment data +- **Data Encryption**: AES-256 encryption for sensitive data +- **Tokenization**: Payment method tokenization for security +- **Audit Logging**: Complete audit trail for all transactions + +### **Authentication & Authorization** + +- **JWT Authentication**: Secure API access with JWT tokens +- **ABAC Authorization**: Attribute-based access control +- **Rate Limiting**: Prevent abuse with rate limiting +- **Input Validation**: Comprehensive input sanitization + +### **Payment Security** + +- **Webhook Verification**: HMAC signature verification +- **Idempotency**: Prevent duplicate charges +- **Fraud Detection**: ML-based fraud prevention (future) +- **Secure Redirects**: HTTPS-only payment redirects + +--- + +## ๐Ÿ“Š **Performance Requirements** + +### **Response Time Targets** + +- **Cart Operations**: < 100ms +- **Order Creation**: < 200ms +- **Payment Session**: < 500ms +- **Webhook Processing**: < 1s +- **Enrollment Creation**: < 2s + +### **Scalability Targets** + +- **Concurrent Users**: 10,000+ simultaneous checkout sessions +- **Throughput**: 1,000+ orders per minute +- **Database**: 100,000+ orders per day +- **Storage**: 1TB+ data storage capacity + +### **Availability Targets** + +- **System Uptime**: 99.9% availability +- **Payment Gateway**: 99.95% uptime +- **Database**: 99.99% uptime +- **Recovery Time**: < 5 minutes for critical failures + +--- + +## ๐Ÿงช **Testing Strategy** + +### **Unit Testing** + +- **Service Layer**: 90%+ code coverage +- **Repository Layer**: Database operation testing +- **Utility Functions**: Helper function testing +- **Mock Integration**: External service mocking + +### **Integration Testing** + +- **API Endpoints**: End-to-end API testing +- **Database Operations**: Transaction testing +- **Payment Gateway**: Sandbox environment testing +- **Webhook Processing**: Event flow testing + +### **Load Testing** + +- **Stress Testing**: Maximum capacity testing +- **Performance Testing**: Response time validation +- **Concurrency Testing**: Race condition testing +- **Failover Testing**: System resilience testing + +### **Security Testing** + +- **Penetration Testing**: Vulnerability assessment +- **OWASP Testing**: OWASP Top 10 compliance +- **Payment Security**: PCI DSS compliance testing +- **Data Protection**: GDPR compliance validation + +--- + +## ๐Ÿ“ˆ **Monitoring & Observability** + +### **Application Monitoring** + +- **Health Checks**: Service health monitoring +- **Performance Metrics**: Response time tracking +- **Error Tracking**: Error rate monitoring +- **Business Metrics**: Conversion rate tracking + +### **Infrastructure Monitoring** + +- **Server Metrics**: CPU, memory, disk usage +- **Database Metrics**: Query performance, connection pools +- **Network Metrics**: Latency, throughput, errors +- **External Services**: Payment gateway status + +### **Business Intelligence** + +- **Conversion Funnel**: Cart-to-enrollment conversion +- **Revenue Tracking**: Real-time revenue metrics +- **User Behavior**: Checkout flow analysis +- **Payment Analytics**: Success rate, method preferences + +--- + +## ๐Ÿš€ **Deployment Strategy** + +### **Environment Setup** + +- **Development**: Local development environment +- **Staging**: Production-like testing environment +- **Production**: Live production environment +- **Disaster Recovery**: Backup and recovery procedures + +### **CI/CD Pipeline** + +- **Code Quality**: Automated code review and testing +- **Security Scanning**: Vulnerability scanning +- **Performance Testing**: Automated performance validation +- **Deployment**: Blue-green deployment strategy + +### **Rollback Strategy** + +- **Feature Flags**: Gradual feature rollout +- **Database Migrations**: Backward-compatible migrations +- **Service Rollback**: Quick service rollback capability +- **Data Recovery**: Point-in-time data recovery + +--- + +## ๐Ÿ“‹ **Implementation Timeline** + +### **Phase 1: Foundation (Day 1 - Hours 1-4)** + +- [ ] Database schema design and implementation +- [ ] Domain models and repository interfaces +- [ ] Basic service layer structure +- [ ] API endpoint scaffolding + +### **Phase 2: Core Services (Day 1 - Hours 5-8)** + +- [ ] Cart service implementation +- [ ] Order service implementation +- [ ] Payment service integration +- [ ] Basic API functionality + +### **Phase 3: Payment Integration (Day 1 - Hours 9-12)** + +- [ ] Razorpay provider completion +- [ ] Stripe provider implementation +- [ ] Webhook handling system +- [ ] Payment flow testing + +### **Phase 4: Event System (Day 1 - Hours 13-16)** + +- [ ] Event publishing implementation +- [ ] Enrollment worker implementation +- [ ] Idempotency middleware +- [ ] End-to-end testing + +### **Phase 5: Production Readiness (Day 2 - Hours 1-8)** + +- [ ] Comprehensive testing +- [ ] Performance optimization +- [ ] Security hardening +- [ ] Documentation completion + +--- + +## ๐ŸŽฏ **Success Criteria** + +### **Functional Success** + +- [ ] Users can successfully add internships to cart +- [ ] Discount codes apply correctly to cart totals +- [ ] Orders are created with proper idempotency +- [ ] Payment processing works with multiple gateways +- [ ] Webhooks trigger enrollment creation +- [ ] No duplicate enrollments or charges occur + +### **Performance Success** + +- [ ] All API endpoints respond within target times +- [ ] System handles 10,000+ concurrent users +- [ ] Payment success rate exceeds 98% +- [ ] Webhook processing achieves 100% reliability + +### **Business Success** + +- [ ] Checkout abandonment rate below 5% +- [ ] Cart-to-enrollment conversion above 85% +- [ ] User satisfaction score above 4.5/5 +- [ ] Revenue tracking accuracy 100% + +--- + +## ๐Ÿšจ **Risk Mitigation** + +### **Technical Risks** + +| Risk | Impact | Mitigation | +| ------------------------ | ------ | --------------------------------- | +| Payment Gateway Downtime | High | Multiple gateway fallback | +| Database Performance | Medium | Query optimization, indexing | +| Webhook Failures | High | Dead letter queue, retry logic | +| Idempotency Issues | High | Comprehensive testing, monitoring | + +### **Business Risks** + +| Risk | Impact | Mitigation | +| ------------------ | -------- | ---------------------------- | +| User Abandonment | High | UX optimization, A/B testing | +| Payment Failures | High | Multiple payment methods | +| Security Breaches | Critical | Security audits, compliance | +| Scalability Issues | Medium | Load testing, auto-scaling | + +--- + +## ๐Ÿ“š **Documentation Requirements** + +### **Technical Documentation** + +- [ ] API documentation with OpenAPI/Swagger +- [ ] Database schema documentation +- [ ] Service architecture documentation +- [ ] Deployment and operations guide + +### **User Documentation** + +- [ ] Checkout flow user guide +- [ ] Payment method instructions +- [ ] Troubleshooting guide +- [ ] FAQ and support documentation + +### **Business Documentation** + +- [ ] Business process documentation +- [ ] Revenue tracking procedures +- [ ] Compliance documentation +- [ ] Audit trail procedures + +--- + +This PRD provides a comprehensive blueprint for implementing a world-class checkout and enrollment system that will drive user satisfaction, increase conversion rates, and provide a solid foundation for business growth. diff --git a/docs/prds/checkout/CHECKOUT_FLOW_DETAILED_DIAGRAM.md b/docs/prds/checkout/CHECKOUT_FLOW_DETAILED_DIAGRAM.md new file mode 100644 index 0000000..75cd228 --- /dev/null +++ b/docs/prds/checkout/CHECKOUT_FLOW_DETAILED_DIAGRAM.md @@ -0,0 +1,456 @@ +# ๐Ÿ›’ **COMPREHENSIVE CHECKOUT FLOW DIAGRAM** + +## ๐Ÿ“Š **Detailed System Flow with Services & APIs** + +```mermaid +flowchart TD + %% SECTION 1: User Journey & Frontend + U1["๐Ÿ‘ค User browses internships"] --> U2["GET /api/v1/internships"] + U2 --> U3["User selects internship"] + U3 --> U4["Click 'Enroll Now'"] + + %% SECTION 2: Cart Creation Flow + U4 --> C1["POST /api-proxy/api/cart"] + C1 --> C2["Payload: {internship_id, period, analytics_data}"] + C2 --> C3["CartService.CreateCart()"] + C3 --> C4["Generate cart_token (UUID)"] + C4 --> C5["Store in Redis + PostgreSQL"] + C5 --> C6["Response: {cart_token, cart_url}"] + + %% SECTION 3: Cart Authentication + C6 --> A1["POST /api/v1/cart/{token}/auth/check"] + A1 --> A2["AuthService.ValidateCartAccess()"] + A2 --> A3{User authenticated?} + A3 -- "No" --> A4["Response: {authenticated: false}"] + A3 -- "Yes" --> A5["Response: {authenticated: true, user_id}"] + + %% SECTION 4: Pricing & Estimation + A4 & A5 --> P1["POST /api/v1/cart/estimate"] + P1 --> P2["Payload: {cart_token}"] + P2 --> P3["PricingService.CalculatePricing()"] + P3 --> P4["CartService.GetCartWithCache()"] + P4 --> P5["DiscountService.ValidateDiscounts()"] + P5 --> P6["TaxService.CalculateTax()"] + P6 --> P7["Response: {subtotal, total, tax_amount, discounts}"] + + %% SECTION 5: Discount Application + P7 --> D1["POST /api/v1/cart/{token}/coupon"] + D1 --> D2["Payload: {coupon: 'SAVE20'}"] + D2 --> D3["DiscountService.ValidateCoupon()"] + D3 --> D4{Coupon valid?} + D4 -- "No" --> D5["Response: {error: 'Invalid coupon'}" ] + D4 -- "Yes" --> D6["CartService.ApplyDiscount()"] + D6 --> D7["Recalculate totals"] + D7 --> D8["Response: {success: true, new_total}"] + + %% SECTION 6: Order Creation + D5 & D8 --> O1["POST /api/v1/order"] + O1 --> O2["Payload: {cart_token, idempotency_key}"] + O2 --> O3["OrderService.CreateFromCart()"] + O3 --> O4["IdempotencyService.CheckKey()"] + O4 --> O5{Key exists?} + O5 -- "Yes" --> O6["Return cached response"] + O5 -- "No" --> O7["CartService.ValidateCart()"] + O7 --> O8["PricingService.GetFinalPricing()"] + O8 --> O9{Payment required?} + + %% SECTION 7: Free Internship Flow + O9 -- "No (Free)" --> F1["OrderService.CreateFreeOrder()"] + F1 --> F2["EnrollmentService.CreateEnrollment()"] + F2 --> F3["NotificationService.SendWelcomeEmail()"] + F3 --> F4["Response: {order_token, status: 'completed'}" ] + + %% SECTION 8: Paid Internship Flow + O9 -- "Yes (Paid)" --> P8["OrderService.CreatePaidOrder()"] + P8 --> P9["PaymentService.CreatePaymentSession()"] + P9 --> P10["RazorpayProvider.CreateOrder()"] + P10 --> P11["Store payment_token"] + P11 --> P12["Response: {order_token, payment_token, checkout_url}"] + + %% SECTION 9: Payment Processing + P12 --> PP1["User redirected to checkout_url"] + PP1 --> PP2["User completes payment"] + PP2 --> PP3["Razorpay webhook: payment.captured"] + PP3 --> W1["POST /api/v1/webhooks/razorpay"] + W1 --> W2["WebhookHandler.VerifySignature()"] + W2 --> W3["WebhookHandler.ProcessPaymentSuccess()"] + W3 --> W4["PaymentService.UpdateStatus()"] + W4 --> W5["OrderService.MarkAsPaid()"] + W5 --> W6["EventPublisher.Publish('order.paid')"] + + %% SECTION 10: Event-Driven Enrollment + W6 --> E1["EnrollmentWorker.Subscribe('order.paid')"] + E1 --> E2["EnrollmentService.CreateEnrollment()"] + E2 --> E3{Enrollment exists?} + E3 -- "Yes" --> E4["Skip (idempotent)"] + E3 -- "No" --> E5["Create enrollment record"] + E5 --> E6["NotificationService.SendConfirmation()"] + E6 --> E7["AnalyticsService.TrackConversion()"] + + %% SECTION 11: Error Handling & Retry + W3 -. "Failure" .- R1["RetryQueue.Publish()"] + R1 --> R2["RetryWorker.Process()"] + R2 --> R3{Max retries?} + R3 -- "No" --> R4["Exponential backoff"] + R3 -- "Yes" --> R5["DeadLetterQueue.Publish()"] + R5 --> R6["AlertService.SendAlert()"] + + %% SECTION 12: Cron Jobs & Cleanup + subgraph "Cron Jobs" + CR1["Every 15min: Cleanup expired carts"] + CR2["Every 30min: Process failed payments"] + CR3["Every hour: Sync payment status"] + CR4["Daily: Generate reports"] + end + + CR1 --> CL1["CartService.CleanupExpired()"] + CL1 --> CL2["Delete carts > 24h old"] + + CR2 --> FP1["PaymentService.RetryFailed()"] + FP1 --> FP2["RazorpayProvider.VerifyStatus()"] + + CR3 --> PS1["OrderService.SyncPaymentStatus()"] + PS1 --> PS2["Update pending orders"] + + %% SECTION 13: Service Layer Details + subgraph "Service Layer" + CS["CartService"] + OS["OrderService"] + PS["PaymentService"] + DS["DiscountService"] + ES["EnrollmentService"] + NS["NotificationService"] + AS["AnalyticsService"] + end + + subgraph "Repository Layer" + CR["CartRepository"] + OR["OrderRepository"] + PR["PaymentRepository"] + DR["DiscountRepository"] + ER["EnrollmentRepository"] + end + + subgraph "External Services" + RP["Razorpay API"] + ST["Stripe API"] + RD["Redis Cache"] + PG["PostgreSQL"] + MQ["Message Queue"] + end + + %% Service Dependencies + CS --> CR + CS --> RD + OS --> OR + OS --> CS + PS --> PR + PS --> RP + PS --> ST + DS --> DR + ES --> ER + ES --> NS + ES --> AS + + %% Data Flow + CR --> PG + OR --> PG + PR --> PG + DR --> PG + ER --> PG + + %% Styling + classDef api fill:#e1f5fe,stroke:#01579b,stroke-width:2px + classDef service fill:#f3e5f5,stroke:#4a148c,stroke-width:2px + classDef repo fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px + classDef external fill:#fff3e0,stroke:#e65100,stroke-width:2px + classDef cron fill:#fce4ec,stroke:#880e4f,stroke-width:2px + + class C1,C2,C6,A1,A4,A5,P1,P2,P7,D1,D2,D5,D8,O1,O2,O6,F4,P12,PP3,W1,R1 api + class C3,C4,C5,A2,P3,P4,P5,P6,D3,D6,D7,O3,O4,O7,O8,O9,F1,F2,F3,P8,P9,P10,P11,W2,W3,W4,W5,W6,E1,E2,E3,E4,E5,E6,E7,R2,R3,R4,R5,R6,CL1,CL2,FP1,FP2,PS1,PS2 service + class CS,OS,PS,DS,ES,NS,AS,CR,OR,PR,DR,ER repo + class RP,ST,RD,PG,MQ external + class CR1,CR2,CR3,CR4 cron +``` + +## ๐Ÿ”„ **Detailed API Flow with JSON Payloads** + +### **1. Cart Creation API** + +```json +// POST /api-proxy/api/cart +{ + "sale_slug": "summer2024", + "products": [ + { + "internship_id": "int_123", + "period": {"value": 3, "unit": "month"} + } + ], + "analytics_data": [ + {"key": "source", "value": "google"}, + {"key": "campaign", "value": "summer_sale"} + ] +} + +// Response +{ + "data": { + "cart": { + "cart_url": "https://cart.interns.com/pay/74f1d2e0-791d-4f59-9a96-ffff785de270" + } + }, + "correlation_id": "9f503084-44d7-4da7-8be4-a673009b388e" +} +``` + +### **2. Cart Authentication API** + +```json +// POST /api/v1/cart/{token}/auth/check +{ + "cart_token": "74f1d2e0-791d-4f59-9a96-ffff785de270" +} + +// Response +{ + "status": 200, + "success": true, + "data": { + "authenticated": false + } +} +``` + +### **3. Cart Estimation API** + +```json +// POST /api/v1/cart/estimate +{ + "cart_token": "74f1d2e0-791d-4f59-9a96-ffff785de270" +} + +// Response +{ + "status": 200, + "success": true, + "data": { + "currency_code": "INR", + "tax_rate": 18, + "sub_total": 16776, + "total": 19796, + "tax_total": 3020, + "discount_amount": 26400, + "discount_percentage": 61, + "items": [ + { + "internship_id": "int_123", + "base_price": 43176, + "total": 16776, + "tax_total": 3020, + "discount_percentage": 61 + } + ] + } +} +``` + +### **4. Coupon Application API** + +```json +// POST /api/v1/cart/{token}/coupon +{ + "coupon": "SAVE20" +} + +// Success Response +{ + "status": 200, + "success": true, + "data": { + "coupon_applied": true, + "discount_amount": 3959, + "new_total": 15837 + } +} + +// Error Response +{ + "status": 422, + "success": false, + "error": { + "code": 2004, + "message": "Coupon is invalid.", + "validation_messages": ["Coupon is invalid."] + } +} +``` + +### **5. Order Creation API** + +```json +// POST /api/v1/order +{ + "cart_token": "74f1d2e0-791d-4f59-9a96-ffff785de270", + "idempotency_key": "idem_123456789" +} + +// Paid Order Response +{ + "data": { + "order": { + "order_token": "41c49b71-c76e-457d-b695-8666639d4426", + "status": "awaiting_payment", + "currency": "INR", + "subtotal": 1197600, + "total": 1413168, + "tax_amount": 215568, + "discount_amount": 1200000, + "payment_token": "aec4c9ce-58a7-46e2-adc3-eebffa8c9908" + } + }, + "status": 200, + "success": true +} + +// Free Order Response +{ + "data": { + "order": { + "order_token": "41c49b71-c76e-457d-b695-8666639d4426", + "status": "completed", + "enrollment_id": "enr_123456789" + } + }, + "status": 200, + "success": true +} +``` + +## ๐Ÿ—๏ธ **Service Layer Architecture** + +### **CartService Responsibilities** + +- Cart CRUD operations +- Cart token generation and validation +- Cart expiration management +- Pricing calculation orchestration +- Cache management (Redis) + +### **OrderService Responsibilities** + +- Order creation from cart +- Order status management +- Payment intent creation +- Free order processing +- Order validation and security + +### **PaymentService Responsibilities** + +- Payment gateway integration +- Payment session creation +- Payment status tracking +- Webhook processing +- Retry mechanisms + +### **EnrollmentService Responsibilities** + +- Enrollment creation after payment +- Idempotency handling +- Batch enrollment processing +- Enrollment status management + +## ๐Ÿ”„ **Error Handling & Recovery** + +### **Retry Mechanisms** + +```go +type RetryConfig struct { + MaxAttempts: 3, + InitialDelay: 5 * time.Second, + MaxDelay: 30 * time.Second, + BackoffFactor: 2.0, + RetryableErrors: [500, 502, 503, 504] +} +``` + +### **Circuit Breaker Pattern** + +```go +type CircuitBreaker struct { + FailureThreshold: 5, + RecoveryTimeout: 60 * time.Second, + State: "CLOSED" | "OPEN" | "HALF_OPEN" +} +``` + +## ๐Ÿ“Š **Cron Jobs & Cleanup** + +### **Scheduled Tasks** + +1. **Cart Cleanup** (Every 15 minutes) + + - Delete expired carts (>24h old) + - Clean up abandoned carts + - Update cart statistics + +2. **Payment Sync** (Every 30 minutes) + + - Sync pending payment status + - Retry failed payments + - Update order status + +3. **Order Cleanup** (Every hour) + + - Mark expired orders + - Clean up orphaned orders + - Generate cleanup reports + +4. **Analytics** (Daily) + - Generate conversion reports + - Calculate revenue metrics + - Update business intelligence + +## ๐Ÿ”’ **Security & Idempotency** + +### **Idempotency Implementation** + +```go +type IdempotencyKey struct { + Key: string, + RequestHash: string, + ResponseBody: []byte, + StatusCode: int, + ExpiresAt: time.Time +} +``` + +### **Security Measures** + +- JWT token validation +- HMAC signature verification +- Rate limiting +- Input sanitization +- CORS policies +- PCI DSS compliance + +## ๐Ÿ“ˆ **Monitoring & Observability** + +### **Key Metrics** + +- Cart creation rate +- Payment success rate +- API response times +- Error rates by service +- Webhook processing latency +- Database performance + +### **Alerting Rules** + +- Payment success rate < 95% +- API response time > 500ms +- Webhook failure rate > 5% +- Database connection pool > 80% +- Cache miss rate > 20% + +This comprehensive diagram shows the complete flow from user interaction to successful enrollment, including all service interactions, error handling, and system maintenance processes. diff --git a/docs/prds/checkout/CRON_JOBS_AND_BACKGROUND_PROCESSING.md b/docs/prds/checkout/CRON_JOBS_AND_BACKGROUND_PROCESSING.md new file mode 100644 index 0000000..89e53c9 --- /dev/null +++ b/docs/prds/checkout/CRON_JOBS_AND_BACKGROUND_PROCESSING.md @@ -0,0 +1,473 @@ +# โฐ **CRON JOBS & BACKGROUND PROCESSING DIAGRAM** + +## ๐Ÿ”„ **Scheduled Tasks & Background Processing Flow** + +```mermaid +graph TB + %% Cron Scheduler + subgraph "Cron Scheduler" + CRON["Cron Scheduler"] + CRON1["Every 15min"] + CRON2["Every 30min"] + CRON3["Every hour"] + CRON4["Daily at 2 AM"] + CRON5["Weekly on Sunday"] + end + + %% Cart Management + subgraph "Cart Management" + CART_CLEANUP["Cart Cleanup Job"] + CART_EXPIRED["Delete Expired Carts"] + CART_ABANDONED["Clean Abandoned Carts"] + CART_STATS["Update Cart Statistics"] + end + + %% Payment Processing + subgraph "Payment Processing" + PAYMENT_SYNC["Payment Sync Job"] + PAYMENT_RETRY["Retry Failed Payments"] + PAYMENT_VERIFY["Verify Payment Status"] + PAYMENT_REPORT["Generate Payment Reports"] + end + + %% Order Management + subgraph "Order Management" + ORDER_CLEANUP["Order Cleanup Job"] + ORDER_EXPIRED["Mark Expired Orders"] + ORDER_ORPHANED["Clean Orphaned Orders"] + ORDER_SYNC["Sync Order Status"] + end + + %% Analytics & Reporting + subgraph "Analytics & Reporting" + ANALYTICS_DAILY["Daily Analytics Job"] + ANALYTICS_WEEKLY["Weekly Analytics Job"] + CONVERSION_REPORT["Conversion Reports"] + REVENUE_REPORT["Revenue Reports"] + BUSINESS_INTEL["Business Intelligence"] + end + + %% Database Maintenance + subgraph "Database Maintenance" + DB_BACKUP["Database Backup"] + DB_OPTIMIZE["Database Optimization"] + DB_CLEANUP["Database Cleanup"] + DB_MONITOR["Database Monitoring"] + end + + %% External Service Health + subgraph "External Services" + WEBHOOK_HEALTH["Webhook Health Check"] + PAYMENT_GATEWAY["Payment Gateway Health"] + EMAIL_SERVICE["Email Service Health"] + CACHE_HEALTH["Cache Health Check"] + end + + %% Cron Triggers + CRON --> CRON1 + CRON --> CRON2 + CRON --> CRON3 + CRON --> CRON4 + CRON --> CRON5 + + %% 15-minute jobs + CRON1 --> CART_CLEANUP + CRON1 --> WEBHOOK_HEALTH + + %% 30-minute jobs + CRON2 --> PAYMENT_SYNC + CRON2 --> PAYMENT_GATEWAY + + %% Hourly jobs + CRON3 --> ORDER_CLEANUP + CRON3 --> EMAIL_SERVICE + CRON3 --> CACHE_HEALTH + + %% Daily jobs + CRON4 --> ANALYTICS_DAILY + CRON4 --> DB_BACKUP + CRON4 --> DB_OPTIMIZE + + %% Weekly jobs + CRON5 --> ANALYTICS_WEEKLY + CRON5 --> DB_CLEANUP + + %% Cart Cleanup Flow + CART_CLEANUP --> CART_EXPIRED + CART_CLEANUP --> CART_ABANDONED + CART_CLEANUP --> CART_STATS + + %% Payment Sync Flow + PAYMENT_SYNC --> PAYMENT_RETRY + PAYMENT_SYNC --> PAYMENT_VERIFY + PAYMENT_SYNC --> PAYMENT_REPORT + + %% Order Cleanup Flow + ORDER_CLEANUP --> ORDER_EXPIRED + ORDER_CLEANUP --> ORDER_ORPHANED + ORDER_CLEANUP --> ORDER_SYNC + + %% Analytics Flow + ANALYTICS_DAILY --> CONVERSION_REPORT + ANALYTICS_DAILY --> REVENUE_REPORT + ANALYTICS_WEEKLY --> BUSINESS_INTEL + + %% Database Flow + DB_BACKUP --> DB_OPTIMIZE + DB_OPTIMIZE --> DB_CLEANUP + DB_CLEANUP --> DB_MONITOR + + %% Styling + classDef cron fill:#fce4ec,stroke:#880e4f,stroke-width:3px + classDef cart fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px + classDef payment fill:#fff3e0,stroke:#f57c00,stroke-width:2px + classDef order fill:#e3f2fd,stroke:#1976d2,stroke-width:2px + classDef analytics fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px + classDef database fill:#ffebee,stroke:#c62828,stroke-width:2px + classDef external fill:#e0f2f1,stroke:#00695c,stroke-width:2px + + class CRON,CRON1,CRON2,CRON3,CRON4,CRON5 cron + class CART_CLEANUP,CART_EXPIRED,CART_ABANDONED,CART_STATS cart + class PAYMENT_SYNC,PAYMENT_RETRY,PAYMENT_VERIFY,PAYMENT_REPORT payment + class ORDER_CLEANUP,ORDER_EXPIRED,ORDER_ORPHANED,ORDER_SYNC order + class ANALYTICS_DAILY,ANALYTICS_WEEKLY,CONVERSION_REPORT,REVENUE_REPORT,BUSINESS_INTEL analytics + class DB_BACKUP,DB_OPTIMIZE,DB_CLEANUP,DB_MONITOR database + class WEBHOOK_HEALTH,PAYMENT_GATEWAY,EMAIL_SERVICE,CACHE_HEALTH external +``` + +## ๐Ÿ“‹ **Detailed Cron Job Specifications** + +### **1. Cart Cleanup Job (Every 15 minutes)** + +```mermaid +sequenceDiagram + participant CRON as Cron Scheduler + participant CS as CartService + participant CR as CartRepository + participant RD as Redis Cache + participant AS as AnalyticsService + + CRON->>CS: CleanupExpiredCarts() + CS->>CR: FindExpiredCarts(24h) + CR-->>CS: List of expired carts + CS->>CR: DeleteCarts(cart_ids) + CS->>RD: DeleteCartTokens(cart_ids) + CS->>AS: TrackCartCleanup(count) + CS-->>CRON: Cleanup completed +``` + +**Implementation:** + +```go +func (cs *CartService) CleanupExpiredCarts() error { + // Find carts older than 24 hours + expiredCarts, err := cs.cartRepo.FindExpiredCarts(24 * time.Hour) + if err != nil { + return err + } + + for _, cart := range expiredCarts { + // Delete from database + if err := cs.cartRepo.DeleteCart(cart.ID); err != nil { + log.Printf("Failed to delete cart %s: %v", cart.ID, err) + continue + } + + // Delete from cache + cs.cache.Delete(fmt.Sprintf("cart:%s", cart.Token)) + + // Track analytics + cs.analyticsSvc.TrackEvent("cart_expired", map[string]interface{}{ + "cart_id": cart.ID, + "internship_id": cart.InternshipID, + "age_hours": time.Since(cart.CreatedAt).Hours(), + }) + } + + return nil +} +``` + +### **2. Payment Sync Job (Every 30 minutes)** + +```mermaid +sequenceDiagram + participant CRON as Cron Scheduler + participant PS as PaymentService + participant PR as PaymentRepository + participant RP as Razorpay API + participant OS as OrderService + participant MQ as Message Queue + + CRON->>PS: SyncPendingPayments() + PS->>PR: FindPendingPayments() + PR-->>PS: List of pending payments + loop For each payment + PS->>RP: GetPaymentStatus(payment_id) + RP-->>PS: Payment status + alt Payment successful + PS->>PR: UpdatePaymentStatus(success) + PS->>OS: MarkOrderAsPaid(order_id) + PS->>MQ: Publish('order.paid') + else Payment failed + PS->>PR: UpdatePaymentStatus(failed) + PS->>MQ: Publish('payment.failed') + end + end + PS-->>CRON: Sync completed +``` + +**Implementation:** + +```go +func (ps *PaymentService) SyncPendingPayments() error { + // Find payments pending for more than 1 hour + pendingPayments, err := ps.paymentRepo.FindPendingPayments(1 * time.Hour) + if err != nil { + return err + } + + for _, payment := range pendingPayments { + // Check status with payment gateway + status, err := ps.razorpay.GetPaymentStatus(payment.GatewayPaymentID) + if err != nil { + log.Printf("Failed to get payment status for %s: %v", payment.ID, err) + continue + } + + // Update payment status + if err := ps.paymentRepo.UpdatePaymentStatus(payment.ID, status); err != nil { + log.Printf("Failed to update payment status for %s: %v", payment.ID, err) + continue + } + + // Handle successful payments + if status == "captured" { + if err := ps.orderService.MarkOrderAsPaid(payment.OrderID); err != nil { + log.Printf("Failed to mark order as paid: %v", err) + } + + // Publish event for enrollment + ps.messageQueue.Publish("order.paid", map[string]interface{}{ + "order_id": payment.OrderID, + "payment_id": payment.ID, + }) + } + } + + return nil +} +``` + +### **3. Order Cleanup Job (Every hour)** + +```mermaid +sequenceDiagram + participant CRON as Cron Scheduler + participant OS as OrderService + participant OR as OrderRepository + participant PS as PaymentService + participant NS as NotificationService + + CRON->>OS: CleanupExpiredOrders() + OS->>OR: FindExpiredOrders(48h) + OR-->>OS: List of expired orders + loop For each order + alt Order has payment + OS->>PS: CancelPayment(order.payment_id) + PS-->>OS: Payment cancelled + end + OS->>OR: MarkOrderExpired(order_id) + OS->>NS: SendExpirationEmail(user_id) + end + OS-->>CRON: Cleanup completed +``` + +### **4. Daily Analytics Job (2 AM daily)** + +```mermaid +sequenceDiagram + participant CRON as Cron Scheduler + participant AS as AnalyticsService + participant AR as AnalyticsRepository + participant OR as OrderRepository + participant CR as CartRepository + participant ER as EnrollmentRepository + + CRON->>AS: GenerateDailyReports() + AS->>OR: GetOrderStats(yesterday) + OR-->>AS: Order statistics + AS->>CR: GetCartStats(yesterday) + CR-->>AS: Cart statistics + AS->>ER: GetEnrollmentStats(yesterday) + ER-->>AS: Enrollment statistics + AS->>AR: StoreDailyReport(stats) + AS->>AS: CalculateConversionRate() + AS->>AS: GenerateRevenueReport() + AS-->>CRON: Reports generated +``` + +## ๐Ÿ”ง **Background Job Implementation** + +### **Job Queue System** + +```go +type JobQueue struct { + redis *redis.Client + workers int + handlers map[string]JobHandler +} + +type JobHandler func(payload []byte) error + +type Job struct { + ID string `json:"id"` + Type string `json:"type"` + Payload map[string]interface{} `json:"payload"` + Priority int `json:"priority"` + CreatedAt time.Time `json:"created_at"` + Retries int `json:"retries"` + MaxRetries int `json:"max_retries"` +} + +func (jq *JobQueue) Enqueue(jobType string, payload map[string]interface{}, priority int) error { + job := Job{ + ID: uuid.New().String(), + Type: jobType, + Payload: payload, + Priority: priority, + CreatedAt: time.Now(), + Retries: 0, + MaxRetries: 3, + } + + jobData, err := json.Marshal(job) + if err != nil { + return err + } + + return jq.redis.LPush(fmt.Sprintf("jobs:%s", jobType), jobData).Err() +} + +func (jq *JobQueue) ProcessJobs() { + for { + // Process high priority jobs first + for _, jobType := range []string{"payment_retry", "enrollment_create", "notification_send"} { + jq.processJobType(jobType) + } + + // Process regular jobs + for _, jobType := range []string{"analytics_track", "email_send", "cache_cleanup"} { + jq.processJobType(jobType) + } + + time.Sleep(1 * time.Second) + } +} +``` + +### **Retry Mechanism with Exponential Backoff** + +```go +func (jq *JobQueue) processJobWithRetry(job *Job) error { + handler, exists := jq.handlers[job.Type] + if !exists { + return fmt.Errorf("no handler for job type: %s", job.Type) + } + + jobData, err := json.Marshal(job.Payload) + if err != nil { + return err + } + + err = handler(jobData) + if err != nil { + if job.Retries < job.MaxRetries { + // Calculate backoff delay + delay := time.Duration(math.Pow(2, float64(job.Retries))) * time.Second + if delay > 30*time.Second { + delay = 30 * time.Second + } + + // Re-queue with retry + job.Retries++ + time.Sleep(delay) + return jq.Enqueue(job.Type, job.Payload, job.Priority) + } + + // Move to dead letter queue + return jq.moveToDeadLetter(job) + } + + return nil +} +``` + +## ๐Ÿ“Š **Monitoring & Alerting** + +### **Job Monitoring** + +```go +type JobMonitor struct { + metrics map[string]*JobMetrics + alerts AlertService +} + +type JobMetrics struct { + TotalJobs int64 + SuccessfulJobs int64 + FailedJobs int64 + AverageTime time.Duration + LastRun time.Time +} + +func (jm *JobMonitor) TrackJob(jobType string, duration time.Duration, success bool) { + metrics := jm.metrics[jobType] + if metrics == nil { + metrics = &JobMetrics{} + jm.metrics[jobType] = metrics + } + + atomic.AddInt64(&metrics.TotalJobs, 1) + if success { + atomic.AddInt64(&metrics.SuccessfulJobs, 1) + } else { + atomic.AddInt64(&metrics.FailedJobs, 1) + } + + // Update average time + metrics.AverageTime = time.Duration( + (int64(metrics.AverageTime) + int64(duration)) / 2, + ) + metrics.LastRun = time.Now() + + // Check for alerts + if metrics.FailedJobs > 10 { + jm.alerts.SendAlert(fmt.Sprintf("Job %s has high failure rate", jobType)) + } +} +``` + +### **Health Check Endpoints** + +```go +func (s *Server) healthCheckHandler(w http.ResponseWriter, r *http.Request) { + health := map[string]interface{}{ + "status": "healthy", + "timestamp": time.Now(), + "services": map[string]interface{}{ + "database": s.checkDatabaseHealth(), + "redis": s.checkRedisHealth(), + "payment_gateway": s.checkPaymentGatewayHealth(), + "email_service": s.checkEmailServiceHealth(), + }, + "cron_jobs": s.getCronJobStatus(), + "background_jobs": s.getBackgroundJobStatus(), + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(health) +} +``` + +This comprehensive cron job and background processing system ensures the checkout flow remains robust, with proper cleanup, monitoring, and error recovery mechanisms in place. diff --git a/docs/prds/checkout/SERVICE_ARCHITECTURE_DIAGRAM.md b/docs/prds/checkout/SERVICE_ARCHITECTURE_DIAGRAM.md new file mode 100644 index 0000000..fca373c --- /dev/null +++ b/docs/prds/checkout/SERVICE_ARCHITECTURE_DIAGRAM.md @@ -0,0 +1,411 @@ +# ๐Ÿ—๏ธ **SERVICE ARCHITECTURE & INTERACTIONS DIAGRAM** + +## ๐Ÿ”„ **Service Layer Interactions & Data Flow** + +```mermaid +graph TB + %% API Layer + subgraph "API Layer" + API1["Cart API"] + API2["Order API"] + API3["Payment API"] + API4["Enrollment API"] + API5["Webhook API"] + end + + %% Service Layer + subgraph "Service Layer" + CS["CartService"] + OS["OrderService"] + PS["PaymentService"] + ES["EnrollmentService"] + DS["DiscountService"] + NS["NotificationService"] + AS["AnalyticsService"] + IS["IdempotencyService"] + WS["WebhookService"] + end + + %% Repository Layer + subgraph "Repository Layer" + CR["CartRepository"] + OR["OrderRepository"] + PR["PaymentRepository"] + ER["EnrollmentRepository"] + DR["DiscountRepository"] + UR["UserRepository"] + IR["InternshipRepository"] + end + + %% External Services + subgraph "External Services" + RP["Razorpay Gateway"] + ST["Stripe Gateway"] + RD["Redis Cache"] + PG["PostgreSQL DB"] + MQ["Message Queue"] + SMTP["SMTP Service"] + end + + %% API to Service Connections + API1 --> CS + API2 --> OS + API3 --> PS + API4 --> ES + API5 --> WS + + %% Service Dependencies + CS --> CR + CS --> RD + CS --> DS + CS --> AS + + OS --> OR + OS --> CS + OS --> PS + OS --> IS + OS --> AS + + PS --> PR + PS --> RP + PS --> ST + PS --> MQ + + ES --> ER + ES --> NS + ES --> AS + ES --> IS + + DS --> DR + WS --> PS + WS --> OS + WS --> ES + + %% Repository to Database + CR --> PG + OR --> PG + PR --> PG + ER --> PG + DR --> PG + UR --> PG + IR --> PG + + %% External Service Connections + NS --> SMTP + MQ --> ES + MQ --> NS + MQ --> AS + + %% Styling + classDef api fill:#e3f2fd,stroke:#1976d2,stroke-width:3px + classDef service fill:#f3e5f5,stroke:#7b1fa2,stroke-width:3px + classDef repo fill:#e8f5e8,stroke:#388e3c,stroke-width:3px + classDef external fill:#fff3e0,stroke:#f57c00,stroke-width:3px + + class API1,API2,API3,API4,API5 api + class CS,OS,PS,ES,DS,NS,AS,IS,WS service + class CR,OR,PR,ER,DR,UR,IR repo + class RP,ST,RD,PG,MQ,SMTP external +``` + +## ๐ŸŽฏ **Scenario-Based Service Interactions** + +### **1. Free Internship Flow** + +```mermaid +sequenceDiagram + participant U as User + participant API as Order API + participant OS as OrderService + participant IS as IdempotencyService + participant CS as CartService + participant ES as EnrollmentService + participant NS as NotificationService + + U->>API: POST /api/v1/order + API->>OS: CreateFromCart(cart_token) + OS->>IS: CheckKey(idempotency_key) + IS-->>OS: Key not found + OS->>CS: ValidateCart(cart_token) + CS-->>OS: Cart valid, total = 0 + OS->>OS: CreateFreeOrder() + OS->>ES: CreateEnrollment(order_data) + ES-->>OS: Enrollment created + OS->>NS: SendWelcomeEmail(user_id) + NS-->>OS: Email sent + OS-->>API: {status: 'completed', enrollment_id} + API-->>U: Order completed +``` + +### **2. Paid Internship Flow** + +```mermaid +sequenceDiagram + participant U as User + participant API as Order API + participant OS as OrderService + participant PS as PaymentService + participant RP as Razorpay + participant MQ as Message Queue + + U->>API: POST /api/v1/order + API->>OS: CreateFromCart(cart_token) + OS->>OS: CreatePaidOrder() + OS->>PS: CreatePaymentSession(order_data) + PS->>RP: CreateOrder(amount, currency) + RP-->>PS: {payment_token, checkout_url} + PS-->>OS: Payment session created + OS->>API: {order_token, payment_token, checkout_url} + API-->>U: Redirect to checkout + + Note over U,RP: User completes payment + RP->>MQ: payment.captured event + MQ->>OS: ProcessPaymentSuccess() + OS->>OS: MarkAsPaid() + OS->>MQ: order.paid event +``` + +### **3. Discount Application Flow** + +```mermaid +sequenceDiagram + participant U as User + participant API as Cart API + participant CS as CartService + participant DS as DiscountService + participant CR as CartRepository + + U->>API: POST /api/v1/cart/{token}/coupon + API->>CS: ApplyDiscount(cart_token, coupon) + CS->>DS: ValidateCoupon(coupon) + DS-->>CS: Coupon valid, 20% off + CS->>CR: UpdateCart(cart_id, discount_data) + CR-->>CS: Cart updated + CS->>CS: RecalculateTotals() + CS-->>API: {success: true, new_total} + API-->>U: Discount applied +``` + +### **4. Webhook Processing Flow** + +```mermaid +sequenceDiagram + participant RP as Razorpay + participant WS as WebhookService + participant PS as PaymentService + participant OS as OrderService + participant ES as EnrollmentService + participant MQ as Message Queue + + RP->>WS: POST /webhooks/razorpay + WS->>WS: VerifySignature(payload) + WS->>PS: ProcessPaymentWebhook(payload) + PS->>PS: UpdatePaymentStatus() + PS->>OS: MarkOrderAsPaid(order_id) + OS->>MQ: Publish('order.paid', order_data) + MQ->>ES: CreateEnrollment(order_data) + ES-->>MQ: Enrollment created + MQ->>ES: SendConfirmationEmail() +``` + +## ๐Ÿ”ง **Service Implementation Details** + +### **CartService Implementation** + +```go +type CartService struct { + cartRepo CartRepository + cache CacheProvider + discountSvc DiscountService + analyticsSvc AnalyticsService +} + +func (cs *CartService) CreateCart(req CreateCartRequest) (*Cart, error) { + // 1. Validate internship exists and is active + // 2. Generate unique cart token + // 3. Calculate initial pricing + // 4. Store in Redis (TTL: 24h) + // 5. Store in PostgreSQL + // 6. Track analytics event +} + +func (cs *CartService) ApplyDiscount(cartToken, coupon string) error { + // 1. Validate cart exists and not expired + // 2. Validate coupon with DiscountService + // 3. Apply discount calculation + // 4. Update cart in cache and DB + // 5. Recalculate totals +} +``` + +### **OrderService Implementation** + +```go +type OrderService struct { + orderRepo OrderRepository + cartSvc CartService + paymentSvc PaymentService + idempotencySvc IdempotencyService + analyticsSvc AnalyticsService +} + +func (os *OrderService) CreateFromCart(cartToken, idempotencyKey string) (*Order, error) { + // 1. Check idempotency key + // 2. Validate cart and get final pricing + // 3. Determine if payment required + // 4. Create order record + // 5. Handle free vs paid flow + // 6. Store idempotency response +} + +func (os *OrderService) CreateFreeOrder(cartData *Cart) (*Order, error) { + // 1. Create order with status 'completed' + // 2. Create enrollment immediately + // 3. Send welcome notification + // 4. Track conversion +} +``` + +### **PaymentService Implementation** + +```go +type PaymentService struct { + paymentRepo PaymentRepository + razorpay RazorpayProvider + stripe StripeProvider + messageQueue MessageQueue +} + +func (ps *PaymentService) CreatePaymentSession(order *Order) (*PaymentSession, error) { + // 1. Determine payment gateway based on amount/currency + // 2. Create payment intent with gateway + // 3. Store payment session + // 4. Return checkout URL +} + +func (ps *PaymentService) ProcessWebhook(payload []byte, signature string) error { + // 1. Verify webhook signature + // 2. Parse payment status + // 3. Update payment record + // 4. Trigger order status update + // 5. Publish event for enrollment +} +``` + +## ๐Ÿ›ก๏ธ **Error Handling & Resilience** + +### **Circuit Breaker Implementation** + +```go +type CircuitBreaker struct { + failureThreshold int + recoveryTimeout time.Duration + state CircuitState + failureCount int + lastFailureTime time.Time +} + +func (cb *CircuitBreaker) Execute(command func() error) error { + if cb.state == Open { + if time.Since(cb.lastFailureTime) > cb.recoveryTimeout { + cb.state = HalfOpen + } else { + return ErrCircuitBreakerOpen + } + } + + err := command() + if err != nil { + cb.recordFailure() + return err + } + + cb.recordSuccess() + return nil +} +``` + +### **Retry Mechanism** + +```go +type RetryConfig struct { + MaxAttempts int + InitialDelay time.Duration + MaxDelay time.Duration + BackoffFactor float64 + RetryableErrors []int +} + +func (rc *RetryConfig) Execute(command func() error) error { + var lastErr error + delay := rc.InitialDelay + + for attempt := 1; attempt <= rc.MaxAttempts; attempt++ { + err := command() + if err == nil { + return nil + } + + lastErr = err + if !rc.isRetryable(err) { + return err + } + + if attempt < rc.MaxAttempts { + time.Sleep(delay) + delay = time.Duration(float64(delay) * rc.BackoffFactor) + if delay > rc.MaxDelay { + delay = rc.MaxDelay + } + } + } + + return lastErr +} +``` + +## ๐Ÿ“Š **Data Models & Relationships** + +### **Cart Entity** + +```go +type Cart struct { + ID string `json:"id"` + Token string `json:"token"` + UserID *string `json:"user_id,omitempty"` + InternshipID string `json:"internship_id"` + Period Period `json:"period"` + Subtotal int64 `json:"subtotal"` + Total int64 `json:"total"` + TaxAmount int64 `json:"tax_amount"` + DiscountAmount int64 `json:"discount_amount"` + CouponCode *string `json:"coupon_code,omitempty"` + Status string `json:"status"` + ExpiresAt time.Time `json:"expires_at"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} +``` + +### **Order Entity** + +```go +type Order struct { + ID string `json:"id"` + Token string `json:"token"` + CartID string `json:"cart_id"` + UserID string `json:"user_id"` + InternshipID string `json:"internship_id"` + Status string `json:"status"` + Subtotal int64 `json:"subtotal"` + Total int64 `json:"total"` + TaxAmount int64 `json:"tax_amount"` + DiscountAmount int64 `json:"discount_amount"` + PaymentToken *string `json:"payment_token,omitempty"` + EnrollmentID *string `json:"enrollment_id,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} +``` + +This architecture ensures clean separation of concerns, robust error handling, and scalable service interactions while maintaining data consistency and system reliability. diff --git a/docs/prds/checkout/flow1.md b/docs/prds/checkout/flow1.md new file mode 100644 index 0000000..f2e0ff4 --- /dev/null +++ b/docs/prds/checkout/flow1.md @@ -0,0 +1,83 @@ +```mermaid +flowchart TD +%% SECTION 1 โ€“ Product Discovery & Cart +U1["๐Ÿ–ฑ๏ธ UI: User browses internships"] --> U2["GET /internships/:id"] +U2 --> U3["Click Enroll โ†’"] +U3 --> C1["POST /cart {internship_id, qty=1, idem_key}"] +C1 --> C2{Cart exists for user?} +C2 -- "Yes" --> C3["Update cart line"] +C2 -- "No" --> C4["Create new cart row (DB INSERT)"] +C3 & C4 --> C5["Return Cart JSON โ†’ FE"] + +%% Discount +C5 --> D1["(Optional) POST /cart/discount {code}"] +D1 --> D2{Code valid & active?} +D2 -- "Yes" --> D3["Apply discount, update totals (DB UPDATE)"] +D2 -- "No" --> D4["400 Invalid coupon"] + +%% SECTION 2 โ€“ Order & Payment Session +D3 --> O1["POST /orders {cart_id} + Idempotency-Key hdr"] +O1 --> O2{idempotency key seen?} +O2 -- "Seen" --> O3["Return stored response (idempotent)"] +O2 -- "New" --> O4["INSERT Order status=PENDING"] +O4 --> O5["INSERT PaymentAttempt status=CREATED"] +O5 --> PG1["Create PaymentIntent via Stripe/Razorpay API"] +PG1 --> O6["Update PaymentAttempt.provider_ref"] +O6 --> FE1["Respond checkout_url, order_id"] +FE1 --> FE2["FE redirects to hosted checkout"] + +%% SECTION 3 โ€“ Gateway Journey +subgraph "Payment Gateway" +FE2 --> PG2["User completes payment"] +PG2 -- "Success" --> PG3["Redirect โ†’ success_url?session_id"] +PG2 -- "Fail/Cancel" --> PG4["Redirect โ†’ cancel_url"] +PG2 -. "Timeout" .- PG5["No redirect (tab closed)"] +PG2 --> WH0["Gateway fires webhook (succeeded/failed/expired)"] +end + +%% SECTION 4 โ€“ Synchronous UX after redirect +PG3 --> UX1["FE hits /orders/:id to poll status"] +PG4 --> UX2["Show failure UI, Retry button"] +PG5 --> UX3["User later opens My-Orders page"] + +%% SECTION 5 โ€“ Webhook Handler (Gin) +subgraph "Webhook Handler" +WH0 --> WH1["Verify HMAC signature"] +WH1 --> WH2{PaymentAttempt already terminal?} +WH2 -- "Yes" --> WH3["200 OK (noop)"] +WH2 -- "No" --> WH4["Tx: UPDATE PaymentAttempt + Order โ†’ PAID/FAILED/EXPIRED"] +WH4 -- "PAID" --> EVT1["Publish OrderPaid evt (Watermill)"] +WH4 -- "FAILED" --> EVT2["Publish OrderFailed evt"] +WH4 -- "EXPIRED" --> EVT3["Publish OrderExpired evt"] +end + +%% SECTION 6 โ€“ Event Driven Enrollment +subgraph "Enrollment Worker" +EVT1 --> EN1["Load order & internship"] +EN1 --> EN2{Enrollment already exists?} +EN2 -- "Yes" --> EN3["Skip & ACK (idempotent)"] +EN2 -- "No" --> EN4["INSERT Enrollment, status=ACTIVE"] +EN4 --> EN5["Send confirmation email + push notif"] +end + +%% SECTION 7 โ€“ Retry & Timeouts +WH1 -.-> WR1["If 5XX โ†’ Gateway retries w/ backoff"] +EVT1 -.-> DLQ1["Watermill redelivers on NACK"] +subgraph "Cron / Watchdog" +CR1["Scan PENDING orders >30m"] --> CR2["Mark EXPIRED & emit OrderExpired evt"] +end + +%% SECTION 8 โ€“ Duplicate Calls / Page Refresh +UX1 -. "User refreshes" .- O1 +FE1 -. "User re-clicks Pay (same idem_key)" .- O2 +FE1 -. "Different idem_key" .- O4 + +%% END STATE +EN5 --> UDone["๐ŸŽ‰ User lands on Internship Dashboard"] + +%% Styling +classDef db fill:#fff4e6,stroke:#e0af69,stroke-width:1px +class C4,D3,O4,O5,O6,EN4 db +classDef ext fill:#e6f7ff,stroke:#8cc8ff +class PG1,PG2,WH0 ext +``` diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index 3e176a2..791f940 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -53,6 +53,707 @@ const docTemplate = `{ } } }, + "/categories": { + "get": { + "description": "List categories with optional filtering", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "List categories", + "parameters": [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "name": "category_ids", + "in": "query" + }, + { + "type": "string", + "name": "end_time", + "in": "query" + }, + { + "type": "string", + "name": "expand", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "name": "internship_ids", + "in": "query" + }, + { + "maximum": 1000, + "minimum": 1, + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "These fields are used to filter categories by name", + "name": "name", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "name": "offset", + "in": "query" + }, + { + "enum": [ + "asc", + "desc" + ], + "type": "string", + "name": "order", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "start_time", + "in": "query" + }, + { + "enum": [ + "published", + "deleted", + "archived", + "inactive", + "pending" + ], + "type": "string", + "x-enum-varnames": [ + "StatusPublished", + "StatusDeleted", + "StatusArchived", + "StatusInactive", + "StatusPending" + ], + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.ListCategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new category with the provided details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "Create a new category", + "parameters": [ + { + "description": "Category details", + "name": "category", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreateCategoryRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.CategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, + "/categories/{id}": { + "get": { + "description": "Get a category by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "Get a category by ID", + "parameters": [ + { + "type": "string", + "description": "Category ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.CategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "put": { + "description": "Update a category by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "Update a category", + "parameters": [ + { + "type": "string", + "description": "Category ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Category details", + "name": "category", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdateCategoryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.CategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Delete a category by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "Delete a category", + "parameters": [ + { + "type": "string", + "description": "Category ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, + "/discounts": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "List discounts with optional filtering", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "List discounts", + "parameters": [ + { + "enum": [ + "flat", + "percentage" + ], + "type": "string", + "x-enum-varnames": [ + "DiscountTypeFlat", + "DiscountTypePercentage" + ], + "name": "discount_type", + "in": "query" + }, + { + "type": "string", + "name": "end_time", + "in": "query" + }, + { + "type": "string", + "name": "expand", + "in": "query" + }, + { + "type": "boolean", + "name": "is_combinable", + "in": "query" + }, + { + "maximum": 1000, + "minimum": 1, + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "number", + "name": "min_order_value", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "name": "offset", + "in": "query" + }, + { + "enum": [ + "asc", + "desc" + ], + "type": "string", + "name": "order", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "start_time", + "in": "query" + }, + { + "enum": [ + "published", + "deleted", + "archived", + "inactive", + "pending" + ], + "type": "string", + "x-enum-varnames": [ + "StatusPublished", + "StatusDeleted", + "StatusArchived", + "StatusInactive", + "StatusPending" + ], + "name": "status", + "in": "query" + }, + { + "type": "string", + "name": "valid_from", + "in": "query" + }, + { + "type": "string", + "name": "valid_until", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.ListDiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Create a new discount with the provided details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Create a new discount", + "parameters": [ + { + "description": "Discount details", + "name": "discount", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreateDiscountRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, + "/discounts/code/{code}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a discount by its unique code", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Get a discount by code", + "parameters": [ + { + "type": "string", + "description": "Discount code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, + "/discounts/{id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a discount by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Get a discount by ID", + "parameters": [ + { + "type": "string", + "description": "Discount ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Update a discount by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Update a discount by ID", + "parameters": [ + { + "type": "string", + "description": "Discount ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Discount details", + "name": "discount", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdateDiscountRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a discount by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Delete a discount by ID", + "parameters": [ + { + "type": "string", + "description": "Discount ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, "/internships": { "get": { "description": "List internships with optional filtering", @@ -501,6 +1202,110 @@ const docTemplate = `{ } }, "definitions": { + "dto.CategoryResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "internships": { + "description": "internships holds the value of the internships edge.", + "type": "array", + "items": { + "$ref": "#/definitions/internship.Internship" + } + }, + "lookup_key": { + "description": "LookupKey holds the value of the \"lookup_key\" field.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "status": { + "$ref": "#/definitions/types.Status" + }, + "updated_at": { + "type": "string" + }, + "updated_by": { + "type": "string" + } + } + }, + "dto.CreateCategoryRequest": { + "type": "object", + "required": [ + "lookup_key", + "name" + ], + "properties": { + "description": { + "type": "string" + }, + "lookup_key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "dto.CreateDiscountRequest": { + "type": "object", + "required": [ + "code", + "discount_type", + "discount_value" + ], + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "discount_type": { + "$ref": "#/definitions/types.DiscountType" + }, + "discount_value": { + "type": "number" + }, + "is_active": { + "type": "boolean" + }, + "is_combinable": { + "type": "boolean" + }, + "max_uses": { + "type": "integer" + }, + "metadata": { + "$ref": "#/definitions/types.Metadata" + }, + "min_order_value": { + "type": "number" + }, + "valid_from": { + "type": "string" + }, + "valid_until": { + "type": "string" + } + } + }, "dto.CreateInternshipRequest": { "type": "object", "required": [ @@ -579,6 +1384,62 @@ const docTemplate = `{ } } }, + "dto.DiscountResponse": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "description": { + "type": "string" + }, + "discount_type": { + "$ref": "#/definitions/types.DiscountType" + }, + "discount_value": { + "type": "number" + }, + "id": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_combinable": { + "type": "boolean" + }, + "max_uses": { + "type": "integer" + }, + "metadata": { + "$ref": "#/definitions/types.Metadata" + }, + "min_order_value": { + "type": "number" + }, + "status": { + "$ref": "#/definitions/types.Status" + }, + "updated_at": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "valid_from": { + "type": "string" + }, + "valid_until": { + "type": "string" + } + } + }, "dto.InternshipResponse": { "type": "object", "properties": { @@ -686,6 +1547,34 @@ const docTemplate = `{ } } }, + "dto.ListCategoryResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.CategoryResponse" + } + }, + "pagination": { + "$ref": "#/definitions/types.PaginationResponse" + } + } + }, + "dto.ListDiscountResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "pagination": { + "$ref": "#/definitions/types.PaginationResponse" + } + } + }, "dto.ListInternshipResponse": { "type": "object", "properties": { @@ -764,6 +1653,49 @@ const docTemplate = `{ } } }, + "dto.UpdateCategoryRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "lookup_key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "dto.UpdateDiscountRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_combinable": { + "type": "boolean" + }, + "max_uses": { + "type": "integer" + }, + "metadata": { + "$ref": "#/definitions/types.Metadata" + }, + "min_order_value": { + "type": "number" + }, + "valid_from": { + "type": "string" + }, + "valid_until": { + "type": "string" + } + } + }, "dto.UpdateInternshipRequest": { "type": "object", "properties": { @@ -888,7 +1820,7 @@ const docTemplate = `{ "type": "string" }, "internships": { - "description": "Internships holds the value of the internships edge.", + "description": "internships holds the value of the internships edge.", "type": "array", "items": { "$ref": "#/definitions/internship.Internship" @@ -1020,6 +1952,17 @@ const docTemplate = `{ } } }, + "types.DiscountType": { + "type": "string", + "enum": [ + "flat", + "percentage" + ], + "x-enum-varnames": [ + "DiscountTypeFlat", + "DiscountTypePercentage" + ] + }, "types.InternshipLevel": { "type": "string", "enum": [ @@ -1046,6 +1989,12 @@ const docTemplate = `{ "InternshipModeOnsite" ] }, + "types.Metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "types.PaginationResponse": { "type": "object", "properties": { @@ -1105,7 +2054,7 @@ const docTemplate = `{ var SwaggerInfo = &swag.Spec{ Version: "1.0", Host: "localhost:8080", - BasePath: "/api/v1", + BasePath: "/v1", Schemes: []string{}, Title: "CodeGeeky API", Description: "API for CodeGeeky", diff --git a/docs/swagger/swagger-3-0.json b/docs/swagger/swagger-3-0.json index 0fb5666..7fad873 100644 --- a/docs/swagger/swagger-3-0.json +++ b/docs/swagger/swagger-3-0.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"CodeGeeky API","description":"API for CodeGeeky","termsOfService":"http://example.com/terms/","contact":{"name":"API Support","email":"support@example.com"},"version":"1.0"},"servers":[{"url":"//localhost:8080/api/v1"}],"paths":{"/auth/signup":{"post":{"tags":["Auth"],"summary":"Signup","description":"Signup","requestBody":{"description":"Signup request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.SignupRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.SignupResponse"}}}}},"x-codegen-request-body-name":"signupRequest"}},"/internships":{"get":{"tags":["Internship"],"summary":"List internships","description":"List internships with optional filtering","parameters":[{"name":"category_ids","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"duration_in_weeks","in":"query","description":"These fields are used to filter internships by duration in weeks","schema":{"maximum":52,"minimum":1,"type":"integer"}},{"name":"end_time","in":"query","schema":{"type":"string"}},{"name":"expand","in":"query","schema":{"type":"string"}},{"name":"internship_ids","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"levels","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string","enum":["beginner","intermediate","advanced"]}}},{"name":"limit","in":"query","schema":{"maximum":1000,"minimum":1,"type":"integer"}},{"name":"max_price","in":"query","description":"These fields are used to filter internships by price","schema":{"type":"number"}},{"name":"min_price","in":"query","schema":{"type":"number"}},{"name":"modes","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string","enum":["remote","hybrid","onsite"]}}},{"name":"name","in":"query","description":"These fields are used to filter internships by category, level and mode","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"minimum":0,"type":"integer"}},{"name":"order","in":"query","schema":{"type":"string","enum":["asc","desc"]}},{"name":"sort","in":"query","schema":{"type":"string"}},{"name":"start_time","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["published","deleted","archived","inactive","pending"],"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]},"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.ListInternshipResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}},"post":{"tags":["Internship"],"summary":"Create a new internship","description":"Create a new internship with the provided details","requestBody":{"description":"Internship details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.CreateInternshipRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.InternshipResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"x-codegen-request-body-name":"internship"}},"/internships/{id}":{"get":{"tags":["Internship"],"summary":"Get an internship by ID","description":"Get an internship by its unique identifier","parameters":[{"name":"id","in":"path","description":"Internship ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.InternshipResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}},"put":{"tags":["Internship"],"summary":"Update an internship","description":"Update an internship by its unique identifier","parameters":[{"name":"id","in":"path","description":"Internship ID","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Internship details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.UpdateInternshipRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.InternshipResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"x-codegen-request-body-name":"internship"},"delete":{"tags":["Internship"],"summary":"Delete an internship","description":"Delete an internship by its unique identifier","parameters":[{"name":"id","in":"path","description":"Internship ID","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content","content":{}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}}},"/user":{"put":{"tags":["User"],"summary":"Update current user","description":"Update the current user's information","requestBody":{"description":"Update user request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.UpdateUserRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.MeResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"x-codegen-request-body-name":"request"}},"/user/me":{"get":{"tags":["User"],"summary":"Get current user","description":"Get the current user's information","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.MeResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}}}},"components":{"schemas":{"dto.CreateInternshipRequest":{"required":["currency","description","level","lookup_key","mode","price","title"],"type":"object","properties":{"benefits":{"type":"array","items":{"type":"string"}},"category_ids":{"type":"array","items":{"type":"string"}},"currency":{"type":"string"},"description":{"minLength":10,"type":"string"},"duration_in_weeks":{"minimum":0,"type":"integer"},"flat_discount":{"type":"number"},"learning_outcomes":{"type":"array","items":{"type":"string"}},"level":{"$ref":"#/components/schemas/types.InternshipLevel"},"lookup_key":{"type":"string"},"mode":{"$ref":"#/components/schemas/types.InternshipMode"},"percentage_discount":{"type":"number"},"prerequisites":{"type":"array","items":{"type":"string"}},"price":{"type":"number"},"skills":{"type":"array","items":{"type":"string"}},"title":{"maxLength":255,"minLength":3,"type":"string"}}},"dto.InternshipResponse":{"type":"object","properties":{"benefits":{"type":"array","description":"Benefits of the internship","items":{"type":"string"}},"categories":{"type":"array","description":"Categories holds the value of the categories edge.","items":{"$ref":"#/components/schemas/internship.Category"}},"created_at":{"type":"string"},"created_by":{"type":"string"},"currency":{"type":"string","description":"Currency of the internship"},"description":{"type":"string","description":"Description holds the value of the \"description\" field."},"duration_in_weeks":{"type":"integer","description":"Alternative to months for shorter internships"},"flat_discount":{"type":"number","description":"Flat discount on the internship"},"id":{"type":"string","description":"ID of the ent."},"learning_outcomes":{"type":"array","description":"What students will learn in the internship","items":{"type":"string"}},"level":{"$ref":"#/components/schemas/types.InternshipLevel"},"lookup_key":{"type":"string","description":"LookupKey holds the value of the \"lookup_key\" field."},"mode":{"$ref":"#/components/schemas/types.InternshipMode"},"percentage_discount":{"type":"number","description":"Percentage discount on the internship"},"prerequisites":{"type":"array","description":"Prerequisites or recommended knowledge","items":{"type":"string"}},"price":{"type":"number","description":"Price of the internship"},"skills":{"type":"array","description":"List of required skills","items":{"type":"string"}},"status":{"$ref":"#/components/schemas/types.Status"},"title":{"type":"string","description":"Title holds the value of the \"title\" field."},"updated_at":{"type":"string"},"updated_by":{"type":"string"}}},"dto.ListInternshipResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/dto.InternshipResponse"}},"pagination":{"$ref":"#/components/schemas/types.PaginationResponse"}}},"dto.MeResponse":{"type":"object","properties":{"email":{"type":"string"},"full_name":{"type":"string"},"id":{"type":"string"},"phone":{"type":"string"},"role":{"type":"string"}}},"dto.SignupRequest":{"required":["access_token","email","full_name","role"],"type":"object","properties":{"access_token":{"type":"string","description":"access token"},"email":{"type":"string","description":"basic info"},"full_name":{"type":"string"},"phone":{"type":"string"},"role":{"$ref":"#/components/schemas/types.UserRole"}}},"dto.SignupResponse":{"type":"object","properties":{"access_token":{"type":"string"},"id":{"type":"string"}}},"dto.UpdateInternshipRequest":{"type":"object","properties":{"benefits":{"type":"array","items":{"type":"string"}},"category_ids":{"type":"array","items":{"type":"string"}},"currency":{"type":"string"},"description":{"minLength":10,"type":"string"},"duration_in_weeks":{"minimum":0,"type":"integer"},"flat_discount":{"type":"number"},"learning_outcomes":{"type":"array","items":{"type":"string"}},"level":{"$ref":"#/components/schemas/types.InternshipLevel"},"lookup_key":{"type":"string"},"mode":{"$ref":"#/components/schemas/types.InternshipMode"},"percentage_discount":{"type":"number"},"prerequisites":{"type":"array","items":{"type":"string"}},"price":{"type":"number"},"skills":{"type":"array","items":{"type":"string"}},"title":{"maxLength":255,"minLength":3,"type":"string"}}},"dto.UpdateUserRequest":{"type":"object","properties":{"full_name":{"type":"string"},"phone":{"type":"string"}}},"ierr.ErrorDetail":{"type":"object","properties":{"details":{"type":"object","additionalProperties":{"type":"object"}},"internal_error":{"type":"string"},"message":{"type":"string"}}},"ierr.ErrorResponse":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/ierr.ErrorDetail"},"success":{"type":"boolean"}}},"internship.Category":{"type":"object","properties":{"created_at":{"type":"string"},"created_by":{"type":"string"},"description":{"type":"string","description":"Description holds the value of the \"description\" field."},"id":{"type":"string","description":"ID of the ent."},"internships":{"type":"array","description":"Internships holds the value of the internships edge.","items":{"$ref":"#/components/schemas/internship.Internship"}},"lookup_key":{"type":"string","description":"LookupKey holds the value of the \"lookup_key\" field."},"name":{"type":"string","description":"Name holds the value of the \"name\" field."},"status":{"$ref":"#/components/schemas/types.Status"},"updated_at":{"type":"string"},"updated_by":{"type":"string"}}},"internship.Internship":{"type":"object","properties":{"benefits":{"type":"array","description":"Benefits of the internship","items":{"type":"string"}},"categories":{"type":"array","description":"Categories holds the value of the categories edge.","items":{"$ref":"#/components/schemas/internship.Category"}},"created_at":{"type":"string"},"created_by":{"type":"string"},"currency":{"type":"string","description":"Currency of the internship"},"description":{"type":"string","description":"Description holds the value of the \"description\" field."},"duration_in_weeks":{"type":"integer","description":"Alternative to months for shorter internships"},"flat_discount":{"type":"number","description":"Flat discount on the internship"},"id":{"type":"string","description":"ID of the ent."},"learning_outcomes":{"type":"array","description":"What students will learn in the internship","items":{"type":"string"}},"level":{"$ref":"#/components/schemas/types.InternshipLevel"},"lookup_key":{"type":"string","description":"LookupKey holds the value of the \"lookup_key\" field."},"mode":{"$ref":"#/components/schemas/types.InternshipMode"},"percentage_discount":{"type":"number","description":"Percentage discount on the internship"},"prerequisites":{"type":"array","description":"Prerequisites or recommended knowledge","items":{"type":"string"}},"price":{"type":"number","description":"Price of the internship"},"skills":{"type":"array","description":"List of required skills","items":{"type":"string"}},"status":{"$ref":"#/components/schemas/types.Status"},"title":{"type":"string","description":"Title holds the value of the \"title\" field."},"updated_at":{"type":"string"},"updated_by":{"type":"string"}}},"types.InternshipLevel":{"type":"string","enum":["beginner","intermediate","advanced"],"x-enum-varnames":["InternshipLevelBeginner","InternshipLevelIntermediate","InternshipLevelAdvanced"]},"types.InternshipMode":{"type":"string","enum":["remote","hybrid","onsite"],"x-enum-varnames":["InternshipModeRemote","InternshipModeHybrid","InternshipModeOnsite"]},"types.PaginationResponse":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total":{"type":"integer"}}},"types.Status":{"type":"string","enum":["published","deleted","archived","inactive","pending"],"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]},"types.UserRole":{"type":"string","enum":["STUDENT","INSTRUCTOR","ADMIN"],"x-enum-varnames":["UserRoleStudent","UserRoleInstructor","UserRoleAdmin"]}},"securitySchemes":{"Authorization":{"type":"apiKey","description":"Enter the token with the `Bearer ` prefix, e.g. `Bearer `.","name":"Authorization","in":"header"}}},"x-original-swagger-version":"2.0"} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"CodeGeeky API","description":"API for CodeGeeky","termsOfService":"http://example.com/terms/","contact":{"name":"API Support","email":"support@example.com"},"version":"1.0"},"servers":[{"url":"//localhost:8080/v1"}],"paths":{"/auth/signup":{"post":{"tags":["Auth"],"summary":"Signup","description":"Signup","requestBody":{"description":"Signup request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.SignupRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.SignupResponse"}}}}},"x-codegen-request-body-name":"signupRequest"}},"/categories":{"get":{"tags":["Category"],"summary":"List categories","description":"List categories with optional filtering","parameters":[{"name":"category_ids","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"end_time","in":"query","schema":{"type":"string"}},{"name":"expand","in":"query","schema":{"type":"string"}},{"name":"internship_ids","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"limit","in":"query","schema":{"maximum":1000,"minimum":1,"type":"integer"}},{"name":"name","in":"query","description":"These fields are used to filter categories by name","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"minimum":0,"type":"integer"}},{"name":"order","in":"query","schema":{"type":"string","enum":["asc","desc"]}},{"name":"sort","in":"query","schema":{"type":"string"}},{"name":"start_time","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["published","deleted","archived","inactive","pending"],"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]},"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.ListCategoryResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}},"post":{"tags":["Category"],"summary":"Create a new category","description":"Create a new category with the provided details","requestBody":{"description":"Category details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.CreateCategoryRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.CategoryResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"x-codegen-request-body-name":"category"}},"/categories/{id}":{"get":{"tags":["Category"],"summary":"Get a category by ID","description":"Get a category by its unique identifier","parameters":[{"name":"id","in":"path","description":"Category ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.CategoryResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}},"put":{"tags":["Category"],"summary":"Update a category","description":"Update a category by its unique identifier","parameters":[{"name":"id","in":"path","description":"Category ID","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Category details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.UpdateCategoryRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.CategoryResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"x-codegen-request-body-name":"category"},"delete":{"tags":["Category"],"summary":"Delete a category","description":"Delete a category by its unique identifier","parameters":[{"name":"id","in":"path","description":"Category ID","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content","content":{}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}}},"/discounts":{"get":{"tags":["Discount"],"summary":"List discounts","description":"List discounts with optional filtering","parameters":[{"name":"discount_type","in":"query","schema":{"type":"string","enum":["flat","percentage"],"x-enum-varnames":["DiscountTypeFlat","DiscountTypePercentage"]},"x-enum-varnames":["DiscountTypeFlat","DiscountTypePercentage"]},{"name":"end_time","in":"query","schema":{"type":"string"}},{"name":"expand","in":"query","schema":{"type":"string"}},{"name":"is_combinable","in":"query","schema":{"type":"boolean"}},{"name":"limit","in":"query","schema":{"maximum":1000,"minimum":1,"type":"integer"}},{"name":"min_order_value","in":"query","schema":{"type":"number"}},{"name":"offset","in":"query","schema":{"minimum":0,"type":"integer"}},{"name":"order","in":"query","schema":{"type":"string","enum":["asc","desc"]}},{"name":"sort","in":"query","schema":{"type":"string"}},{"name":"start_time","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["published","deleted","archived","inactive","pending"],"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]},"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]},{"name":"valid_from","in":"query","schema":{"type":"string"}},{"name":"valid_until","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.ListDiscountResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"security":[{"ApiKeyAuth":[]}]},"post":{"tags":["Discount"],"summary":"Create a new discount","description":"Create a new discount with the provided details","requestBody":{"description":"Discount details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.CreateDiscountRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.DiscountResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"security":[{"ApiKeyAuth":[]}],"x-codegen-request-body-name":"discount"}},"/discounts/code/{code}":{"get":{"tags":["Discount"],"summary":"Get a discount by code","description":"Get a discount by its unique code","parameters":[{"name":"code","in":"path","description":"Discount code","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.DiscountResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"security":[{"ApiKeyAuth":[]}]}},"/discounts/{id}":{"get":{"tags":["Discount"],"summary":"Get a discount by ID","description":"Get a discount by its unique identifier","parameters":[{"name":"id","in":"path","description":"Discount ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.DiscountResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"security":[{"ApiKeyAuth":[]}]},"put":{"tags":["Discount"],"summary":"Update a discount by ID","description":"Update a discount by its unique identifier","parameters":[{"name":"id","in":"path","description":"Discount ID","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Discount details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.UpdateDiscountRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.DiscountResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"security":[{"ApiKeyAuth":[]}],"x-codegen-request-body-name":"discount"},"delete":{"tags":["Discount"],"summary":"Delete a discount by ID","description":"Delete a discount by its unique identifier","parameters":[{"name":"id","in":"path","description":"Discount ID","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content","content":{}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"security":[{"ApiKeyAuth":[]}]}},"/internships":{"get":{"tags":["Internship"],"summary":"List internships","description":"List internships with optional filtering","parameters":[{"name":"category_ids","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"duration_in_weeks","in":"query","description":"These fields are used to filter internships by duration in weeks","schema":{"maximum":52,"minimum":1,"type":"integer"}},{"name":"end_time","in":"query","schema":{"type":"string"}},{"name":"expand","in":"query","schema":{"type":"string"}},{"name":"internship_ids","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"levels","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string","enum":["beginner","intermediate","advanced"]}}},{"name":"limit","in":"query","schema":{"maximum":1000,"minimum":1,"type":"integer"}},{"name":"max_price","in":"query","description":"These fields are used to filter internships by price","schema":{"type":"number"}},{"name":"min_price","in":"query","schema":{"type":"number"}},{"name":"modes","in":"query","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string","enum":["remote","hybrid","onsite"]}}},{"name":"name","in":"query","description":"These fields are used to filter internships by category, level and mode","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"minimum":0,"type":"integer"}},{"name":"order","in":"query","schema":{"type":"string","enum":["asc","desc"]}},{"name":"sort","in":"query","schema":{"type":"string"}},{"name":"start_time","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["published","deleted","archived","inactive","pending"],"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]},"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.ListInternshipResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}},"post":{"tags":["Internship"],"summary":"Create a new internship","description":"Create a new internship with the provided details","requestBody":{"description":"Internship details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.CreateInternshipRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.InternshipResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"x-codegen-request-body-name":"internship"}},"/internships/{id}":{"get":{"tags":["Internship"],"summary":"Get an internship by ID","description":"Get an internship by its unique identifier","parameters":[{"name":"id","in":"path","description":"Internship ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.InternshipResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}},"put":{"tags":["Internship"],"summary":"Update an internship","description":"Update an internship by its unique identifier","parameters":[{"name":"id","in":"path","description":"Internship ID","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Internship details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.UpdateInternshipRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.InternshipResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"x-codegen-request-body-name":"internship"},"delete":{"tags":["Internship"],"summary":"Delete an internship","description":"Delete an internship by its unique identifier","parameters":[{"name":"id","in":"path","description":"Internship ID","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content","content":{}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}}},"/user":{"put":{"tags":["User"],"summary":"Update current user","description":"Update the current user's information","requestBody":{"description":"Update user request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.UpdateUserRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.MeResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}},"x-codegen-request-body-name":"request"}},"/user/me":{"get":{"tags":["User"],"summary":"Get current user","description":"Get the current user's information","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/dto.MeResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ierr.ErrorResponse"}}}}}}}},"components":{"schemas":{"dto.CategoryResponse":{"type":"object","properties":{"created_at":{"type":"string"},"created_by":{"type":"string"},"description":{"type":"string","description":"Description holds the value of the \"description\" field."},"id":{"type":"string","description":"ID of the ent."},"internships":{"type":"array","description":"internships holds the value of the internships edge.","items":{"$ref":"#/components/schemas/internship.Internship"}},"lookup_key":{"type":"string","description":"LookupKey holds the value of the \"lookup_key\" field."},"name":{"type":"string","description":"Name holds the value of the \"name\" field."},"status":{"$ref":"#/components/schemas/types.Status"},"updated_at":{"type":"string"},"updated_by":{"type":"string"}}},"dto.CreateCategoryRequest":{"required":["lookup_key","name"],"type":"object","properties":{"description":{"type":"string"},"lookup_key":{"type":"string"},"name":{"type":"string"}}},"dto.CreateDiscountRequest":{"required":["code","discount_type","discount_value"],"type":"object","properties":{"code":{"type":"string"},"description":{"type":"string"},"discount_type":{"$ref":"#/components/schemas/types.DiscountType"},"discount_value":{"type":"number"},"is_active":{"type":"boolean"},"is_combinable":{"type":"boolean"},"max_uses":{"type":"integer"},"metadata":{"$ref":"#/components/schemas/types.Metadata"},"min_order_value":{"type":"number"},"valid_from":{"type":"string"},"valid_until":{"type":"string"}}},"dto.CreateInternshipRequest":{"required":["currency","description","level","lookup_key","mode","price","title"],"type":"object","properties":{"benefits":{"type":"array","items":{"type":"string"}},"category_ids":{"type":"array","items":{"type":"string"}},"currency":{"type":"string"},"description":{"minLength":10,"type":"string"},"duration_in_weeks":{"minimum":0,"type":"integer"},"flat_discount":{"type":"number"},"learning_outcomes":{"type":"array","items":{"type":"string"}},"level":{"$ref":"#/components/schemas/types.InternshipLevel"},"lookup_key":{"type":"string"},"mode":{"$ref":"#/components/schemas/types.InternshipMode"},"percentage_discount":{"type":"number"},"prerequisites":{"type":"array","items":{"type":"string"}},"price":{"type":"number"},"skills":{"type":"array","items":{"type":"string"}},"title":{"maxLength":255,"minLength":3,"type":"string"}}},"dto.DiscountResponse":{"type":"object","properties":{"code":{"type":"string"},"created_at":{"type":"string"},"created_by":{"type":"string"},"description":{"type":"string"},"discount_type":{"$ref":"#/components/schemas/types.DiscountType"},"discount_value":{"type":"number"},"id":{"type":"string"},"is_active":{"type":"boolean"},"is_combinable":{"type":"boolean"},"max_uses":{"type":"integer"},"metadata":{"$ref":"#/components/schemas/types.Metadata"},"min_order_value":{"type":"number"},"status":{"$ref":"#/components/schemas/types.Status"},"updated_at":{"type":"string"},"updated_by":{"type":"string"},"valid_from":{"type":"string"},"valid_until":{"type":"string"}}},"dto.InternshipResponse":{"type":"object","properties":{"benefits":{"type":"array","description":"Benefits of the internship","items":{"type":"string"}},"categories":{"type":"array","description":"Categories holds the value of the categories edge.","items":{"$ref":"#/components/schemas/internship.Category"}},"created_at":{"type":"string"},"created_by":{"type":"string"},"currency":{"type":"string","description":"Currency of the internship"},"description":{"type":"string","description":"Description holds the value of the \"description\" field."},"duration_in_weeks":{"type":"integer","description":"Alternative to months for shorter internships"},"flat_discount":{"type":"number","description":"Flat discount on the internship"},"id":{"type":"string","description":"ID of the ent."},"learning_outcomes":{"type":"array","description":"What students will learn in the internship","items":{"type":"string"}},"level":{"$ref":"#/components/schemas/types.InternshipLevel"},"lookup_key":{"type":"string","description":"LookupKey holds the value of the \"lookup_key\" field."},"mode":{"$ref":"#/components/schemas/types.InternshipMode"},"percentage_discount":{"type":"number","description":"Percentage discount on the internship"},"prerequisites":{"type":"array","description":"Prerequisites or recommended knowledge","items":{"type":"string"}},"price":{"type":"number","description":"Price of the internship"},"skills":{"type":"array","description":"List of required skills","items":{"type":"string"}},"status":{"$ref":"#/components/schemas/types.Status"},"title":{"type":"string","description":"Title holds the value of the \"title\" field."},"updated_at":{"type":"string"},"updated_by":{"type":"string"}}},"dto.ListCategoryResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/dto.CategoryResponse"}},"pagination":{"$ref":"#/components/schemas/types.PaginationResponse"}}},"dto.ListDiscountResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/dto.DiscountResponse"}},"pagination":{"$ref":"#/components/schemas/types.PaginationResponse"}}},"dto.ListInternshipResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/dto.InternshipResponse"}},"pagination":{"$ref":"#/components/schemas/types.PaginationResponse"}}},"dto.MeResponse":{"type":"object","properties":{"email":{"type":"string"},"full_name":{"type":"string"},"id":{"type":"string"},"phone":{"type":"string"},"role":{"type":"string"}}},"dto.SignupRequest":{"required":["access_token","email","full_name","role"],"type":"object","properties":{"access_token":{"type":"string","description":"access token"},"email":{"type":"string","description":"basic info"},"full_name":{"type":"string"},"phone":{"type":"string"},"role":{"$ref":"#/components/schemas/types.UserRole"}}},"dto.SignupResponse":{"type":"object","properties":{"access_token":{"type":"string"},"id":{"type":"string"}}},"dto.UpdateCategoryRequest":{"type":"object","properties":{"description":{"type":"string"},"lookup_key":{"type":"string"},"name":{"type":"string"}}},"dto.UpdateDiscountRequest":{"type":"object","properties":{"description":{"type":"string"},"is_active":{"type":"boolean"},"is_combinable":{"type":"boolean"},"max_uses":{"type":"integer"},"metadata":{"$ref":"#/components/schemas/types.Metadata"},"min_order_value":{"type":"number"},"valid_from":{"type":"string"},"valid_until":{"type":"string"}}},"dto.UpdateInternshipRequest":{"type":"object","properties":{"benefits":{"type":"array","items":{"type":"string"}},"category_ids":{"type":"array","items":{"type":"string"}},"currency":{"type":"string"},"description":{"minLength":10,"type":"string"},"duration_in_weeks":{"minimum":0,"type":"integer"},"flat_discount":{"type":"number"},"learning_outcomes":{"type":"array","items":{"type":"string"}},"level":{"$ref":"#/components/schemas/types.InternshipLevel"},"lookup_key":{"type":"string"},"mode":{"$ref":"#/components/schemas/types.InternshipMode"},"percentage_discount":{"type":"number"},"prerequisites":{"type":"array","items":{"type":"string"}},"price":{"type":"number"},"skills":{"type":"array","items":{"type":"string"}},"title":{"maxLength":255,"minLength":3,"type":"string"}}},"dto.UpdateUserRequest":{"type":"object","properties":{"full_name":{"type":"string"},"phone":{"type":"string"}}},"ierr.ErrorDetail":{"type":"object","properties":{"details":{"type":"object","additionalProperties":{"type":"object"}},"internal_error":{"type":"string"},"message":{"type":"string"}}},"ierr.ErrorResponse":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/ierr.ErrorDetail"},"success":{"type":"boolean"}}},"internship.Category":{"type":"object","properties":{"created_at":{"type":"string"},"created_by":{"type":"string"},"description":{"type":"string","description":"Description holds the value of the \"description\" field."},"id":{"type":"string","description":"ID of the ent."},"internships":{"type":"array","description":"internships holds the value of the internships edge.","items":{"$ref":"#/components/schemas/internship.Internship"}},"lookup_key":{"type":"string","description":"LookupKey holds the value of the \"lookup_key\" field."},"name":{"type":"string","description":"Name holds the value of the \"name\" field."},"status":{"$ref":"#/components/schemas/types.Status"},"updated_at":{"type":"string"},"updated_by":{"type":"string"}}},"internship.Internship":{"type":"object","properties":{"benefits":{"type":"array","description":"Benefits of the internship","items":{"type":"string"}},"categories":{"type":"array","description":"Categories holds the value of the categories edge.","items":{"$ref":"#/components/schemas/internship.Category"}},"created_at":{"type":"string"},"created_by":{"type":"string"},"currency":{"type":"string","description":"Currency of the internship"},"description":{"type":"string","description":"Description holds the value of the \"description\" field."},"duration_in_weeks":{"type":"integer","description":"Alternative to months for shorter internships"},"flat_discount":{"type":"number","description":"Flat discount on the internship"},"id":{"type":"string","description":"ID of the ent."},"learning_outcomes":{"type":"array","description":"What students will learn in the internship","items":{"type":"string"}},"level":{"$ref":"#/components/schemas/types.InternshipLevel"},"lookup_key":{"type":"string","description":"LookupKey holds the value of the \"lookup_key\" field."},"mode":{"$ref":"#/components/schemas/types.InternshipMode"},"percentage_discount":{"type":"number","description":"Percentage discount on the internship"},"prerequisites":{"type":"array","description":"Prerequisites or recommended knowledge","items":{"type":"string"}},"price":{"type":"number","description":"Price of the internship"},"skills":{"type":"array","description":"List of required skills","items":{"type":"string"}},"status":{"$ref":"#/components/schemas/types.Status"},"title":{"type":"string","description":"Title holds the value of the \"title\" field."},"updated_at":{"type":"string"},"updated_by":{"type":"string"}}},"types.DiscountType":{"type":"string","enum":["flat","percentage"],"x-enum-varnames":["DiscountTypeFlat","DiscountTypePercentage"]},"types.InternshipLevel":{"type":"string","enum":["beginner","intermediate","advanced"],"x-enum-varnames":["InternshipLevelBeginner","InternshipLevelIntermediate","InternshipLevelAdvanced"]},"types.InternshipMode":{"type":"string","enum":["remote","hybrid","onsite"],"x-enum-varnames":["InternshipModeRemote","InternshipModeHybrid","InternshipModeOnsite"]},"types.Metadata":{"type":"object","additionalProperties":{"type":"string"}},"types.PaginationResponse":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total":{"type":"integer"}}},"types.Status":{"type":"string","enum":["published","deleted","archived","inactive","pending"],"x-enum-varnames":["StatusPublished","StatusDeleted","StatusArchived","StatusInactive","StatusPending"]},"types.UserRole":{"type":"string","enum":["STUDENT","INSTRUCTOR","ADMIN"],"x-enum-varnames":["UserRoleStudent","UserRoleInstructor","UserRoleAdmin"]}},"securitySchemes":{"Authorization":{"type":"apiKey","description":"Enter the token with the `Bearer ` prefix, e.g. `Bearer `.","name":"Authorization","in":"header"}}},"x-original-swagger-version":"2.0"} \ No newline at end of file diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 9e24275..d5c3a7e 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -11,7 +11,7 @@ "version": "1.0" }, "host": "localhost:8080", - "basePath": "/api/v1", + "basePath": "/v1", "paths": { "/auth/signup": { "post": { @@ -47,6 +47,707 @@ } } }, + "/categories": { + "get": { + "description": "List categories with optional filtering", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "List categories", + "parameters": [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "name": "category_ids", + "in": "query" + }, + { + "type": "string", + "name": "end_time", + "in": "query" + }, + { + "type": "string", + "name": "expand", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "name": "internship_ids", + "in": "query" + }, + { + "maximum": 1000, + "minimum": 1, + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "These fields are used to filter categories by name", + "name": "name", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "name": "offset", + "in": "query" + }, + { + "enum": [ + "asc", + "desc" + ], + "type": "string", + "name": "order", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "start_time", + "in": "query" + }, + { + "enum": [ + "published", + "deleted", + "archived", + "inactive", + "pending" + ], + "type": "string", + "x-enum-varnames": [ + "StatusPublished", + "StatusDeleted", + "StatusArchived", + "StatusInactive", + "StatusPending" + ], + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.ListCategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new category with the provided details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "Create a new category", + "parameters": [ + { + "description": "Category details", + "name": "category", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreateCategoryRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.CategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, + "/categories/{id}": { + "get": { + "description": "Get a category by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "Get a category by ID", + "parameters": [ + { + "type": "string", + "description": "Category ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.CategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "put": { + "description": "Update a category by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "Update a category", + "parameters": [ + { + "type": "string", + "description": "Category ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Category details", + "name": "category", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdateCategoryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.CategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Delete a category by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Category" + ], + "summary": "Delete a category", + "parameters": [ + { + "type": "string", + "description": "Category ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, + "/discounts": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "List discounts with optional filtering", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "List discounts", + "parameters": [ + { + "enum": [ + "flat", + "percentage" + ], + "type": "string", + "x-enum-varnames": [ + "DiscountTypeFlat", + "DiscountTypePercentage" + ], + "name": "discount_type", + "in": "query" + }, + { + "type": "string", + "name": "end_time", + "in": "query" + }, + { + "type": "string", + "name": "expand", + "in": "query" + }, + { + "type": "boolean", + "name": "is_combinable", + "in": "query" + }, + { + "maximum": 1000, + "minimum": 1, + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "number", + "name": "min_order_value", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "name": "offset", + "in": "query" + }, + { + "enum": [ + "asc", + "desc" + ], + "type": "string", + "name": "order", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "start_time", + "in": "query" + }, + { + "enum": [ + "published", + "deleted", + "archived", + "inactive", + "pending" + ], + "type": "string", + "x-enum-varnames": [ + "StatusPublished", + "StatusDeleted", + "StatusArchived", + "StatusInactive", + "StatusPending" + ], + "name": "status", + "in": "query" + }, + { + "type": "string", + "name": "valid_from", + "in": "query" + }, + { + "type": "string", + "name": "valid_until", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.ListDiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Create a new discount with the provided details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Create a new discount", + "parameters": [ + { + "description": "Discount details", + "name": "discount", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreateDiscountRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, + "/discounts/code/{code}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a discount by its unique code", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Get a discount by code", + "parameters": [ + { + "type": "string", + "description": "Discount code", + "name": "code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, + "/discounts/{id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a discount by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Get a discount by ID", + "parameters": [ + { + "type": "string", + "description": "Discount ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Update a discount by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Update a discount by ID", + "parameters": [ + { + "type": "string", + "description": "Discount ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Discount details", + "name": "discount", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdateDiscountRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a discount by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discount" + ], + "summary": "Delete a discount by ID", + "parameters": [ + { + "type": "string", + "description": "Discount ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ierr.ErrorResponse" + } + } + } + } + }, "/internships": { "get": { "description": "List internships with optional filtering", @@ -495,6 +1196,110 @@ } }, "definitions": { + "dto.CategoryResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "internships": { + "description": "internships holds the value of the internships edge.", + "type": "array", + "items": { + "$ref": "#/definitions/internship.Internship" + } + }, + "lookup_key": { + "description": "LookupKey holds the value of the \"lookup_key\" field.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "status": { + "$ref": "#/definitions/types.Status" + }, + "updated_at": { + "type": "string" + }, + "updated_by": { + "type": "string" + } + } + }, + "dto.CreateCategoryRequest": { + "type": "object", + "required": [ + "lookup_key", + "name" + ], + "properties": { + "description": { + "type": "string" + }, + "lookup_key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "dto.CreateDiscountRequest": { + "type": "object", + "required": [ + "code", + "discount_type", + "discount_value" + ], + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "discount_type": { + "$ref": "#/definitions/types.DiscountType" + }, + "discount_value": { + "type": "number" + }, + "is_active": { + "type": "boolean" + }, + "is_combinable": { + "type": "boolean" + }, + "max_uses": { + "type": "integer" + }, + "metadata": { + "$ref": "#/definitions/types.Metadata" + }, + "min_order_value": { + "type": "number" + }, + "valid_from": { + "type": "string" + }, + "valid_until": { + "type": "string" + } + } + }, "dto.CreateInternshipRequest": { "type": "object", "required": [ @@ -573,6 +1378,62 @@ } } }, + "dto.DiscountResponse": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "description": { + "type": "string" + }, + "discount_type": { + "$ref": "#/definitions/types.DiscountType" + }, + "discount_value": { + "type": "number" + }, + "id": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_combinable": { + "type": "boolean" + }, + "max_uses": { + "type": "integer" + }, + "metadata": { + "$ref": "#/definitions/types.Metadata" + }, + "min_order_value": { + "type": "number" + }, + "status": { + "$ref": "#/definitions/types.Status" + }, + "updated_at": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "valid_from": { + "type": "string" + }, + "valid_until": { + "type": "string" + } + } + }, "dto.InternshipResponse": { "type": "object", "properties": { @@ -672,6 +1533,34 @@ } } }, + "dto.ListCategoryResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.CategoryResponse" + } + }, + "pagination": { + "$ref": "#/definitions/types.PaginationResponse" + } + } + }, + "dto.ListDiscountResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.DiscountResponse" + } + }, + "pagination": { + "$ref": "#/definitions/types.PaginationResponse" + } + } + }, "dto.ListInternshipResponse": { "type": "object", "properties": { @@ -746,6 +1635,49 @@ } } }, + "dto.UpdateCategoryRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "lookup_key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "dto.UpdateDiscountRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_combinable": { + "type": "boolean" + }, + "max_uses": { + "type": "integer" + }, + "metadata": { + "$ref": "#/definitions/types.Metadata" + }, + "min_order_value": { + "type": "number" + }, + "valid_from": { + "type": "string" + }, + "valid_until": { + "type": "string" + } + } + }, "dto.UpdateInternshipRequest": { "type": "object", "properties": { @@ -870,7 +1802,7 @@ "type": "string" }, "internships": { - "description": "Internships holds the value of the internships edge.", + "description": "internships holds the value of the internships edge.", "type": "array", "items": { "$ref": "#/definitions/internship.Internship" @@ -994,6 +1926,17 @@ } } }, + "types.DiscountType": { + "type": "string", + "enum": [ + "flat", + "percentage" + ], + "x-enum-varnames": [ + "DiscountTypeFlat", + "DiscountTypePercentage" + ] + }, "types.InternshipLevel": { "type": "string", "enum": [ @@ -1020,6 +1963,12 @@ "InternshipModeOnsite" ] }, + "types.Metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "types.PaginationResponse": { "type": "object", "properties": { diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index f79f663..0b349a7 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -1,5 +1,76 @@ -basePath: /api/v1 +basePath: /v1 definitions: + dto.CategoryResponse: + properties: + created_at: + type: string + created_by: + type: string + description: + description: Description holds the value of the "description" field. + type: string + id: + description: ID of the ent. + type: string + internships: + description: internships holds the value of the internships edge. + items: + $ref: '#/definitions/internship.Internship' + type: array + lookup_key: + description: LookupKey holds the value of the "lookup_key" field. + type: string + name: + description: Name holds the value of the "name" field. + type: string + status: + $ref: '#/definitions/types.Status' + updated_at: + type: string + updated_by: + type: string + type: object + dto.CreateCategoryRequest: + properties: + description: + type: string + lookup_key: + type: string + name: + type: string + required: + - lookup_key + - name + type: object + dto.CreateDiscountRequest: + properties: + code: + type: string + description: + type: string + discount_type: + $ref: '#/definitions/types.DiscountType' + discount_value: + type: number + is_active: + type: boolean + is_combinable: + type: boolean + max_uses: + type: integer + metadata: + $ref: '#/definitions/types.Metadata' + min_order_value: + type: number + valid_from: + type: string + valid_until: + type: string + required: + - code + - discount_type + - discount_value + type: object dto.CreateInternshipRequest: properties: benefits: @@ -55,6 +126,43 @@ definitions: - price - title type: object + dto.DiscountResponse: + properties: + code: + type: string + created_at: + type: string + created_by: + type: string + description: + type: string + discount_type: + $ref: '#/definitions/types.DiscountType' + discount_value: + type: number + id: + type: string + is_active: + type: boolean + is_combinable: + type: boolean + max_uses: + type: integer + metadata: + $ref: '#/definitions/types.Metadata' + min_order_value: + type: number + status: + $ref: '#/definitions/types.Status' + updated_at: + type: string + updated_by: + type: string + valid_from: + type: string + valid_until: + type: string + type: object dto.InternshipResponse: properties: benefits: @@ -128,6 +236,24 @@ definitions: updated_by: type: string type: object + dto.ListCategoryResponse: + properties: + items: + items: + $ref: '#/definitions/dto.CategoryResponse' + type: array + pagination: + $ref: '#/definitions/types.PaginationResponse' + type: object + dto.ListDiscountResponse: + properties: + items: + items: + $ref: '#/definitions/dto.DiscountResponse' + type: array + pagination: + $ref: '#/definitions/types.PaginationResponse' + type: object dto.ListInternshipResponse: properties: items: @@ -179,6 +305,34 @@ definitions: id: type: string type: object + dto.UpdateCategoryRequest: + properties: + description: + type: string + lookup_key: + type: string + name: + type: string + type: object + dto.UpdateDiscountRequest: + properties: + description: + type: string + is_active: + type: boolean + is_combinable: + type: boolean + max_uses: + type: integer + metadata: + $ref: '#/definitions/types.Metadata' + min_order_value: + type: number + valid_from: + type: string + valid_until: + type: string + type: object dto.UpdateInternshipRequest: properties: benefits: @@ -263,7 +417,7 @@ definitions: description: ID of the ent. type: string internships: - description: Internships holds the value of the internships edge. + description: internships holds the value of the internships edge. items: $ref: '#/definitions/internship.Internship' type: array @@ -353,6 +507,14 @@ definitions: updated_by: type: string type: object + types.DiscountType: + enum: + - flat + - percentage + type: string + x-enum-varnames: + - DiscountTypeFlat + - DiscountTypePercentage types.InternshipLevel: enum: - beginner @@ -373,6 +535,10 @@ definitions: - InternshipModeRemote - InternshipModeHybrid - InternshipModeOnsite + types.Metadata: + additionalProperties: + type: string + type: object types.PaginationResponse: properties: limit: @@ -438,6 +604,464 @@ paths: summary: Signup tags: - Auth + /categories: + get: + consumes: + - application/json + description: List categories with optional filtering + parameters: + - collectionFormat: csv + in: query + items: + type: string + name: category_ids + type: array + - in: query + name: end_time + type: string + - in: query + name: expand + type: string + - collectionFormat: csv + in: query + items: + type: string + name: internship_ids + type: array + - in: query + maximum: 1000 + minimum: 1 + name: limit + type: integer + - description: These fields are used to filter categories by name + in: query + name: name + type: string + - in: query + minimum: 0 + name: offset + type: integer + - enum: + - asc + - desc + in: query + name: order + type: string + - in: query + name: sort + type: string + - in: query + name: start_time + type: string + - enum: + - published + - deleted + - archived + - inactive + - pending + in: query + name: status + type: string + x-enum-varnames: + - StatusPublished + - StatusDeleted + - StatusArchived + - StatusInactive + - StatusPending + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.ListCategoryResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + summary: List categories + tags: + - Category + post: + consumes: + - application/json + description: Create a new category with the provided details + parameters: + - description: Category details + in: body + name: category + required: true + schema: + $ref: '#/definitions/dto.CreateCategoryRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/dto.CategoryResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + summary: Create a new category + tags: + - Category + /categories/{id}: + delete: + consumes: + - application/json + description: Delete a category by its unique identifier + parameters: + - description: Category ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + summary: Delete a category + tags: + - Category + get: + consumes: + - application/json + description: Get a category by its unique identifier + parameters: + - description: Category ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.CategoryResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + summary: Get a category by ID + tags: + - Category + put: + consumes: + - application/json + description: Update a category by its unique identifier + parameters: + - description: Category ID + in: path + name: id + required: true + type: string + - description: Category details + in: body + name: category + required: true + schema: + $ref: '#/definitions/dto.UpdateCategoryRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.CategoryResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + summary: Update a category + tags: + - Category + /discounts: + get: + consumes: + - application/json + description: List discounts with optional filtering + parameters: + - enum: + - flat + - percentage + in: query + name: discount_type + type: string + x-enum-varnames: + - DiscountTypeFlat + - DiscountTypePercentage + - in: query + name: end_time + type: string + - in: query + name: expand + type: string + - in: query + name: is_combinable + type: boolean + - in: query + maximum: 1000 + minimum: 1 + name: limit + type: integer + - in: query + name: min_order_value + type: number + - in: query + minimum: 0 + name: offset + type: integer + - enum: + - asc + - desc + in: query + name: order + type: string + - in: query + name: sort + type: string + - in: query + name: start_time + type: string + - enum: + - published + - deleted + - archived + - inactive + - pending + in: query + name: status + type: string + x-enum-varnames: + - StatusPublished + - StatusDeleted + - StatusArchived + - StatusInactive + - StatusPending + - in: query + name: valid_from + type: string + - in: query + name: valid_until + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.ListDiscountResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: List discounts + tags: + - Discount + post: + consumes: + - application/json + description: Create a new discount with the provided details + parameters: + - description: Discount details + in: body + name: discount + required: true + schema: + $ref: '#/definitions/dto.CreateDiscountRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/dto.DiscountResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Create a new discount + tags: + - Discount + /discounts/{id}: + delete: + consumes: + - application/json + description: Delete a discount by its unique identifier + parameters: + - description: Discount ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Delete a discount by ID + tags: + - Discount + get: + consumes: + - application/json + description: Get a discount by its unique identifier + parameters: + - description: Discount ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.DiscountResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Get a discount by ID + tags: + - Discount + put: + consumes: + - application/json + description: Update a discount by its unique identifier + parameters: + - description: Discount ID + in: path + name: id + required: true + type: string + - description: Discount details + in: body + name: discount + required: true + schema: + $ref: '#/definitions/dto.UpdateDiscountRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.DiscountResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Update a discount by ID + tags: + - Discount + /discounts/code/{code}: + get: + consumes: + - application/json + description: Get a discount by its unique code + parameters: + - description: Discount code + in: path + name: code + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.DiscountResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ierr.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ierr.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Get a discount by code + tags: + - Discount /internships: get: consumes: diff --git a/ellipsis.yaml b/ellipsis.yaml new file mode 100644 index 0000000..c401eee --- /dev/null +++ b/ellipsis.yaml @@ -0,0 +1,83 @@ +version: 1.3 +about: + - "Our golang stack" + - go version "1.24.0" + - go.mod + - gin for rest apis + - entgo.io/ent for database ORM and schema management + - github.com/lib/pq for postgres database access + - github.com/ThreeDotsLabs/watermill for pub/sub message processing + - github.com/razorpay/razorpay-go for payment processing + - github.com/cloudinary/cloudinary-go/v2 for file uploads + - github.com/nedpals/supabase-go for authentication + - "we are using golang with well defined interfaces and dependency injection using fx" + - "Our file structure" + - cmd/server/main.go: entry point for the server + - "This is responsible for starting the server and initializing all dependencies using fx dependency injection" + - internal/api/v1: all rest api handlers defined here" + - "This is responsible for validating input request DTOS and calling service layer" + - internal/api/dto: all data transfer objects defined here" + - "This is responsible for defining request and response DTOS with required validations and types" + - internal/api/router.go: all rest api router defined here" + - "This is responsible for defining all api routes and their handlers" + - internal/config: all configuration defined here" + - "This is responsible for loading configuration from environment variables and files using viper" + - internal/logger: logging wrapper on top of zap.SugaredLogger" + - internal/postgres: postgres client wrapper with ent ORM + - internal/domain: all domain models and repository interfaces defined here" + - "This contains business logic models for cart, internship, payment, user, etc." + - ent/schema: all ent schemas defined here" + - "This is mapped with the domain model as well by implementing a func FromEnt(ent *ent.Schema) DomainModel function" + - "internal/repository" + - internal/repository/ent: all ent repository implementations defined here + - This is responsible for interacting with postgres database using ent ORM + - internal/repository/factory.go: all repository factory defined here + - This is responsible for choosing the correct repository based on the configuration + - internal/service: all service logic defined here + - This is responsible for calling the repository layer and performing business logic + - This layer can call other service layers if needed + - internal/auth: authentication and authorization system + - "This includes RBAC, ABAC, and Supabase integration for auth" + - internal/payment: payment gateway system + - "This includes Razorpay integration and payment processing logic" + - internal/webhook: webhook system for external integrations + - "This handles webhook publishing, subscribing, and payload management" + - internal/pubsub: pub/sub system for event-driven architecture + - "This includes memory-based pub/sub and message routing" + - internal/fileupload: file upload system using Cloudinary + - "This handles file uploads, storage, and management" + - internal/cache: caching system using go-cache + - "This provides in-memory caching capabilities" + - internal/security: security utilities including encryption + - "This handles encryption and other security-related functionality" + - internal/validator: request validation using go-playground/validator + - "This provides request validation capabilities" + - internal/errors: error handling and custom error types + - "This defines custom error types and error handling utilities" + - internal/types: common types and interfaces + - "This contains shared types, enums, and interfaces used across the system" + - internal/testutil: testing utilities and mocks + - "This provides testing utilities, mocks, and test suites" + - internal/httpclient: HTTP client wrapper + - "This provides HTTP client functionality for external API calls" + - internal/idempotency: idempotency utilities + - "This handles idempotency for API requests" + - internal/utils: utility functions + - "This contains common utility functions used across the system" + - migrations: all migrations defined here + - migrations/postgres: all postgres migrations defined here + - This is responsible for creating and running postgres migrations + - Whenever creating a new domain model, add a new migration + - docs/swagger: API documentation using swaggo + - "This contains auto-generated Swagger documentation for the API" +ignore: + - "ent/" # ignore everything in the `ent/` directory as most code is auto generated + - "*.json" + - "*.yaml" + - "*.yml" + - "*.toml" + - "*.ini" + - "*.env" + - "*.lock" + - "*.log" + - "*.txt" diff --git a/ent/cart.go b/ent/cart.go new file mode 100644 index 0000000..a912d9d --- /dev/null +++ b/ent/cart.go @@ -0,0 +1,289 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/user" + "github.com/shopspring/decimal" +) + +// Cart is the model entity for the Cart schema. +type Cart struct { + config `json:"-"` + // ID of the ent. + ID string `json:"id,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // UpdatedBy holds the value of the "updated_by" field. + UpdatedBy string `json:"updated_by,omitempty"` + // Metadata holds the value of the "metadata" field. + Metadata map[string]string `json:"metadata,omitempty"` + // UserID holds the value of the "user_id" field. + UserID string `json:"user_id,omitempty"` + // Type holds the value of the "type" field. + Type string `json:"type,omitempty"` + // Subtotal holds the value of the "subtotal" field. + Subtotal decimal.Decimal `json:"subtotal,omitempty"` + // DiscountAmount holds the value of the "discount_amount" field. + DiscountAmount decimal.Decimal `json:"discount_amount,omitempty"` + // TaxAmount holds the value of the "tax_amount" field. + TaxAmount decimal.Decimal `json:"tax_amount,omitempty"` + // Total holds the value of the "total" field. + Total decimal.Decimal `json:"total,omitempty"` + // ExpiresAt holds the value of the "expires_at" field. + ExpiresAt time.Time `json:"expires_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CartQuery when eager-loading is set. + Edges CartEdges `json:"edges"` + selectValues sql.SelectValues +} + +// CartEdges holds the relations/edges for other nodes in the graph. +type CartEdges struct { + // LineItems holds the value of the line_items edge. + LineItems []*CartLineItems `json:"line_items,omitempty"` + // User holds the value of the user edge. + User *User `json:"user,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// LineItemsOrErr returns the LineItems value or an error if the edge +// was not loaded in eager-loading. +func (e CartEdges) LineItemsOrErr() ([]*CartLineItems, error) { + if e.loadedTypes[0] { + return e.LineItems, nil + } + return nil, &NotLoadedError{edge: "line_items"} +} + +// UserOrErr returns the User value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e CartEdges) UserOrErr() (*User, error) { + if e.User != nil { + return e.User, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: user.Label} + } + return nil, &NotLoadedError{edge: "user"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Cart) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case cart.FieldMetadata: + values[i] = new([]byte) + case cart.FieldSubtotal, cart.FieldDiscountAmount, cart.FieldTaxAmount, cart.FieldTotal: + values[i] = new(decimal.Decimal) + case cart.FieldID, cart.FieldStatus, cart.FieldCreatedBy, cart.FieldUpdatedBy, cart.FieldUserID, cart.FieldType: + values[i] = new(sql.NullString) + case cart.FieldCreatedAt, cart.FieldUpdatedAt, cart.FieldExpiresAt: + values[i] = new(sql.NullTime) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Cart fields. +func (c *Cart) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case cart.FieldID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value.Valid { + c.ID = value.String + } + case cart.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + c.Status = value.String + } + case cart.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + c.CreatedAt = value.Time + } + case cart.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + c.UpdatedAt = value.Time + } + case cart.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + c.CreatedBy = value.String + } + case cart.FieldUpdatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field updated_by", values[i]) + } else if value.Valid { + c.UpdatedBy = value.String + } + case cart.FieldMetadata: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field metadata", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &c.Metadata); err != nil { + return fmt.Errorf("unmarshal field metadata: %w", err) + } + } + case cart.FieldUserID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field user_id", values[i]) + } else if value.Valid { + c.UserID = value.String + } + case cart.FieldType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field type", values[i]) + } else if value.Valid { + c.Type = value.String + } + case cart.FieldSubtotal: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field subtotal", values[i]) + } else if value != nil { + c.Subtotal = *value + } + case cart.FieldDiscountAmount: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field discount_amount", values[i]) + } else if value != nil { + c.DiscountAmount = *value + } + case cart.FieldTaxAmount: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field tax_amount", values[i]) + } else if value != nil { + c.TaxAmount = *value + } + case cart.FieldTotal: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field total", values[i]) + } else if value != nil { + c.Total = *value + } + case cart.FieldExpiresAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field expires_at", values[i]) + } else if value.Valid { + c.ExpiresAt = value.Time + } + default: + c.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Cart. +// This includes values selected through modifiers, order, etc. +func (c *Cart) Value(name string) (ent.Value, error) { + return c.selectValues.Get(name) +} + +// QueryLineItems queries the "line_items" edge of the Cart entity. +func (c *Cart) QueryLineItems() *CartLineItemsQuery { + return NewCartClient(c.config).QueryLineItems(c) +} + +// QueryUser queries the "user" edge of the Cart entity. +func (c *Cart) QueryUser() *UserQuery { + return NewCartClient(c.config).QueryUser(c) +} + +// Update returns a builder for updating this Cart. +// Note that you need to call Cart.Unwrap() before calling this method if this Cart +// was returned from a transaction, and the transaction was committed or rolled back. +func (c *Cart) Update() *CartUpdateOne { + return NewCartClient(c.config).UpdateOne(c) +} + +// Unwrap unwraps the Cart entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (c *Cart) Unwrap() *Cart { + _tx, ok := c.config.driver.(*txDriver) + if !ok { + panic("ent: Cart is not a transactional entity") + } + c.config.driver = _tx.drv + return c +} + +// String implements the fmt.Stringer. +func (c *Cart) String() string { + var builder strings.Builder + builder.WriteString("Cart(") + builder.WriteString(fmt.Sprintf("id=%v, ", c.ID)) + builder.WriteString("status=") + builder.WriteString(c.Status) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(c.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(c.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(c.CreatedBy) + builder.WriteString(", ") + builder.WriteString("updated_by=") + builder.WriteString(c.UpdatedBy) + builder.WriteString(", ") + builder.WriteString("metadata=") + builder.WriteString(fmt.Sprintf("%v", c.Metadata)) + builder.WriteString(", ") + builder.WriteString("user_id=") + builder.WriteString(c.UserID) + builder.WriteString(", ") + builder.WriteString("type=") + builder.WriteString(c.Type) + builder.WriteString(", ") + builder.WriteString("subtotal=") + builder.WriteString(fmt.Sprintf("%v", c.Subtotal)) + builder.WriteString(", ") + builder.WriteString("discount_amount=") + builder.WriteString(fmt.Sprintf("%v", c.DiscountAmount)) + builder.WriteString(", ") + builder.WriteString("tax_amount=") + builder.WriteString(fmt.Sprintf("%v", c.TaxAmount)) + builder.WriteString(", ") + builder.WriteString("total=") + builder.WriteString(fmt.Sprintf("%v", c.Total)) + builder.WriteString(", ") + builder.WriteString("expires_at=") + builder.WriteString(c.ExpiresAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Carts is a parsable slice of Cart. +type Carts []*Cart diff --git a/ent/cart/cart.go b/ent/cart/cart.go new file mode 100644 index 0000000..9caf946 --- /dev/null +++ b/ent/cart/cart.go @@ -0,0 +1,222 @@ +// Code generated by ent, DO NOT EDIT. + +package cart + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/shopspring/decimal" +) + +const ( + // Label holds the string label denoting the cart type in the database. + Label = "cart" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldUpdatedBy holds the string denoting the updated_by field in the database. + FieldUpdatedBy = "updated_by" + // FieldMetadata holds the string denoting the metadata field in the database. + FieldMetadata = "metadata" + // FieldUserID holds the string denoting the user_id field in the database. + FieldUserID = "user_id" + // FieldType holds the string denoting the type field in the database. + FieldType = "type" + // FieldSubtotal holds the string denoting the subtotal field in the database. + FieldSubtotal = "subtotal" + // FieldDiscountAmount holds the string denoting the discount_amount field in the database. + FieldDiscountAmount = "discount_amount" + // FieldTaxAmount holds the string denoting the tax_amount field in the database. + FieldTaxAmount = "tax_amount" + // FieldTotal holds the string denoting the total field in the database. + FieldTotal = "total" + // FieldExpiresAt holds the string denoting the expires_at field in the database. + FieldExpiresAt = "expires_at" + // EdgeLineItems holds the string denoting the line_items edge name in mutations. + EdgeLineItems = "line_items" + // EdgeUser holds the string denoting the user edge name in mutations. + EdgeUser = "user" + // Table holds the table name of the cart in the database. + Table = "carts" + // LineItemsTable is the table that holds the line_items relation/edge. + LineItemsTable = "cart_line_items" + // LineItemsInverseTable is the table name for the CartLineItems entity. + // It exists in this package in order to avoid circular dependency with the "cartlineitems" package. + LineItemsInverseTable = "cart_line_items" + // LineItemsColumn is the table column denoting the line_items relation/edge. + LineItemsColumn = "cart_id" + // UserTable is the table that holds the user relation/edge. + UserTable = "carts" + // UserInverseTable is the table name for the User entity. + // It exists in this package in order to avoid circular dependency with the "user" package. + UserInverseTable = "users" + // UserColumn is the table column denoting the user relation/edge. + UserColumn = "user_id" +) + +// Columns holds all SQL columns for cart fields. +var Columns = []string{ + FieldID, + FieldStatus, + FieldCreatedAt, + FieldUpdatedAt, + FieldCreatedBy, + FieldUpdatedBy, + FieldMetadata, + FieldUserID, + FieldType, + FieldSubtotal, + FieldDiscountAmount, + FieldTaxAmount, + FieldTotal, + FieldExpiresAt, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultMetadata holds the default value on creation for the "metadata" field. + DefaultMetadata map[string]string + // UserIDValidator is a validator for the "user_id" field. It is called by the builders before save. + UserIDValidator func(string) error + // TypeValidator is a validator for the "type" field. It is called by the builders before save. + TypeValidator func(string) error + // DefaultSubtotal holds the default value on creation for the "subtotal" field. + DefaultSubtotal decimal.Decimal + // DefaultDiscountAmount holds the default value on creation for the "discount_amount" field. + DefaultDiscountAmount decimal.Decimal + // DefaultTaxAmount holds the default value on creation for the "tax_amount" field. + DefaultTaxAmount decimal.Decimal + // DefaultTotal holds the default value on creation for the "total" field. + DefaultTotal decimal.Decimal + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() string +) + +// OrderOption defines the ordering options for the Cart queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByUpdatedBy orders the results by the updated_by field. +func ByUpdatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedBy, opts...).ToFunc() +} + +// ByUserID orders the results by the user_id field. +func ByUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUserID, opts...).ToFunc() +} + +// ByType orders the results by the type field. +func ByType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldType, opts...).ToFunc() +} + +// BySubtotal orders the results by the subtotal field. +func BySubtotal(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubtotal, opts...).ToFunc() +} + +// ByDiscountAmount orders the results by the discount_amount field. +func ByDiscountAmount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDiscountAmount, opts...).ToFunc() +} + +// ByTaxAmount orders the results by the tax_amount field. +func ByTaxAmount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTaxAmount, opts...).ToFunc() +} + +// ByTotal orders the results by the total field. +func ByTotal(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTotal, opts...).ToFunc() +} + +// ByExpiresAt orders the results by the expires_at field. +func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExpiresAt, opts...).ToFunc() +} + +// ByLineItemsCount orders the results by line_items count. +func ByLineItemsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newLineItemsStep(), opts...) + } +} + +// ByLineItems orders the results by line_items terms. +func ByLineItems(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLineItemsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByUserField orders the results by user field. +func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...)) + } +} +func newLineItemsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LineItemsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, LineItemsTable, LineItemsColumn), + ) +} +func newUserStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn), + ) +} diff --git a/ent/cart/where.go b/ent/cart/where.go new file mode 100644 index 0000000..ab2e548 --- /dev/null +++ b/ent/cart/where.go @@ -0,0 +1,823 @@ +// Code generated by ent, DO NOT EDIT. + +package cart + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/omkar273/codegeeky/ent/predicate" + "github.com/shopspring/decimal" +) + +// ID filters vertices based on their ID field. +func ID(id string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id string) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...string) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...string) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id string) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id string) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id string) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id string) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldID, id)) +} + +// IDEqualFold applies the EqualFold predicate on the ID field. +func IDEqualFold(id string) predicate.Cart { + return predicate.Cart(sql.FieldEqualFold(FieldID, id)) +} + +// IDContainsFold applies the ContainsFold predicate on the ID field. +func IDContainsFold(id string) predicate.Cart { + return predicate.Cart(sql.FieldContainsFold(FieldID, id)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldStatus, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldCreatedBy, v)) +} + +// UpdatedBy applies equality check predicate on the "updated_by" field. It's identical to UpdatedByEQ. +func UpdatedBy(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ. +func UserID(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldUserID, v)) +} + +// Type applies equality check predicate on the "type" field. It's identical to TypeEQ. +func Type(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldType, v)) +} + +// Subtotal applies equality check predicate on the "subtotal" field. It's identical to SubtotalEQ. +func Subtotal(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldSubtotal, v)) +} + +// DiscountAmount applies equality check predicate on the "discount_amount" field. It's identical to DiscountAmountEQ. +func DiscountAmount(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldDiscountAmount, v)) +} + +// TaxAmount applies equality check predicate on the "tax_amount" field. It's identical to TaxAmountEQ. +func TaxAmount(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldTaxAmount, v)) +} + +// Total applies equality check predicate on the "total" field. It's identical to TotalEQ. +func Total(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldTotal, v)) +} + +// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. +func ExpiresAt(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldExpiresAt, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.Cart { + return predicate.Cart(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldContainsFold(FieldStatus, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.Cart { + return predicate.Cart(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.Cart { + return predicate.Cart(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.Cart { + return predicate.Cart(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// UpdatedByEQ applies the EQ predicate on the "updated_by" field. +func UpdatedByEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UpdatedByNEQ applies the NEQ predicate on the "updated_by" field. +func UpdatedByNEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldUpdatedBy, v)) +} + +// UpdatedByIn applies the In predicate on the "updated_by" field. +func UpdatedByIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByNotIn applies the NotIn predicate on the "updated_by" field. +func UpdatedByNotIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByGT applies the GT predicate on the "updated_by" field. +func UpdatedByGT(v string) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldUpdatedBy, v)) +} + +// UpdatedByGTE applies the GTE predicate on the "updated_by" field. +func UpdatedByGTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldUpdatedBy, v)) +} + +// UpdatedByLT applies the LT predicate on the "updated_by" field. +func UpdatedByLT(v string) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldUpdatedBy, v)) +} + +// UpdatedByLTE applies the LTE predicate on the "updated_by" field. +func UpdatedByLTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldUpdatedBy, v)) +} + +// UpdatedByContains applies the Contains predicate on the "updated_by" field. +func UpdatedByContains(v string) predicate.Cart { + return predicate.Cart(sql.FieldContains(FieldUpdatedBy, v)) +} + +// UpdatedByHasPrefix applies the HasPrefix predicate on the "updated_by" field. +func UpdatedByHasPrefix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasPrefix(FieldUpdatedBy, v)) +} + +// UpdatedByHasSuffix applies the HasSuffix predicate on the "updated_by" field. +func UpdatedByHasSuffix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasSuffix(FieldUpdatedBy, v)) +} + +// UpdatedByIsNil applies the IsNil predicate on the "updated_by" field. +func UpdatedByIsNil() predicate.Cart { + return predicate.Cart(sql.FieldIsNull(FieldUpdatedBy)) +} + +// UpdatedByNotNil applies the NotNil predicate on the "updated_by" field. +func UpdatedByNotNil() predicate.Cart { + return predicate.Cart(sql.FieldNotNull(FieldUpdatedBy)) +} + +// UpdatedByEqualFold applies the EqualFold predicate on the "updated_by" field. +func UpdatedByEqualFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldEqualFold(FieldUpdatedBy, v)) +} + +// UpdatedByContainsFold applies the ContainsFold predicate on the "updated_by" field. +func UpdatedByContainsFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldContainsFold(FieldUpdatedBy, v)) +} + +// MetadataIsNil applies the IsNil predicate on the "metadata" field. +func MetadataIsNil() predicate.Cart { + return predicate.Cart(sql.FieldIsNull(FieldMetadata)) +} + +// MetadataNotNil applies the NotNil predicate on the "metadata" field. +func MetadataNotNil() predicate.Cart { + return predicate.Cart(sql.FieldNotNull(FieldMetadata)) +} + +// UserIDEQ applies the EQ predicate on the "user_id" field. +func UserIDEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldUserID, v)) +} + +// UserIDNEQ applies the NEQ predicate on the "user_id" field. +func UserIDNEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldUserID, v)) +} + +// UserIDIn applies the In predicate on the "user_id" field. +func UserIDIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldUserID, vs...)) +} + +// UserIDNotIn applies the NotIn predicate on the "user_id" field. +func UserIDNotIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldUserID, vs...)) +} + +// UserIDGT applies the GT predicate on the "user_id" field. +func UserIDGT(v string) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldUserID, v)) +} + +// UserIDGTE applies the GTE predicate on the "user_id" field. +func UserIDGTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldUserID, v)) +} + +// UserIDLT applies the LT predicate on the "user_id" field. +func UserIDLT(v string) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldUserID, v)) +} + +// UserIDLTE applies the LTE predicate on the "user_id" field. +func UserIDLTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldUserID, v)) +} + +// UserIDContains applies the Contains predicate on the "user_id" field. +func UserIDContains(v string) predicate.Cart { + return predicate.Cart(sql.FieldContains(FieldUserID, v)) +} + +// UserIDHasPrefix applies the HasPrefix predicate on the "user_id" field. +func UserIDHasPrefix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasPrefix(FieldUserID, v)) +} + +// UserIDHasSuffix applies the HasSuffix predicate on the "user_id" field. +func UserIDHasSuffix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasSuffix(FieldUserID, v)) +} + +// UserIDEqualFold applies the EqualFold predicate on the "user_id" field. +func UserIDEqualFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldEqualFold(FieldUserID, v)) +} + +// UserIDContainsFold applies the ContainsFold predicate on the "user_id" field. +func UserIDContainsFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldContainsFold(FieldUserID, v)) +} + +// TypeEQ applies the EQ predicate on the "type" field. +func TypeEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldType, v)) +} + +// TypeNEQ applies the NEQ predicate on the "type" field. +func TypeNEQ(v string) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldType, v)) +} + +// TypeIn applies the In predicate on the "type" field. +func TypeIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldType, vs...)) +} + +// TypeNotIn applies the NotIn predicate on the "type" field. +func TypeNotIn(vs ...string) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldType, vs...)) +} + +// TypeGT applies the GT predicate on the "type" field. +func TypeGT(v string) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldType, v)) +} + +// TypeGTE applies the GTE predicate on the "type" field. +func TypeGTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldType, v)) +} + +// TypeLT applies the LT predicate on the "type" field. +func TypeLT(v string) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldType, v)) +} + +// TypeLTE applies the LTE predicate on the "type" field. +func TypeLTE(v string) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldType, v)) +} + +// TypeContains applies the Contains predicate on the "type" field. +func TypeContains(v string) predicate.Cart { + return predicate.Cart(sql.FieldContains(FieldType, v)) +} + +// TypeHasPrefix applies the HasPrefix predicate on the "type" field. +func TypeHasPrefix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasPrefix(FieldType, v)) +} + +// TypeHasSuffix applies the HasSuffix predicate on the "type" field. +func TypeHasSuffix(v string) predicate.Cart { + return predicate.Cart(sql.FieldHasSuffix(FieldType, v)) +} + +// TypeEqualFold applies the EqualFold predicate on the "type" field. +func TypeEqualFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldEqualFold(FieldType, v)) +} + +// TypeContainsFold applies the ContainsFold predicate on the "type" field. +func TypeContainsFold(v string) predicate.Cart { + return predicate.Cart(sql.FieldContainsFold(FieldType, v)) +} + +// SubtotalEQ applies the EQ predicate on the "subtotal" field. +func SubtotalEQ(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldSubtotal, v)) +} + +// SubtotalNEQ applies the NEQ predicate on the "subtotal" field. +func SubtotalNEQ(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldSubtotal, v)) +} + +// SubtotalIn applies the In predicate on the "subtotal" field. +func SubtotalIn(vs ...decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldSubtotal, vs...)) +} + +// SubtotalNotIn applies the NotIn predicate on the "subtotal" field. +func SubtotalNotIn(vs ...decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldSubtotal, vs...)) +} + +// SubtotalGT applies the GT predicate on the "subtotal" field. +func SubtotalGT(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldSubtotal, v)) +} + +// SubtotalGTE applies the GTE predicate on the "subtotal" field. +func SubtotalGTE(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldSubtotal, v)) +} + +// SubtotalLT applies the LT predicate on the "subtotal" field. +func SubtotalLT(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldSubtotal, v)) +} + +// SubtotalLTE applies the LTE predicate on the "subtotal" field. +func SubtotalLTE(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldSubtotal, v)) +} + +// DiscountAmountEQ applies the EQ predicate on the "discount_amount" field. +func DiscountAmountEQ(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldDiscountAmount, v)) +} + +// DiscountAmountNEQ applies the NEQ predicate on the "discount_amount" field. +func DiscountAmountNEQ(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldDiscountAmount, v)) +} + +// DiscountAmountIn applies the In predicate on the "discount_amount" field. +func DiscountAmountIn(vs ...decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldDiscountAmount, vs...)) +} + +// DiscountAmountNotIn applies the NotIn predicate on the "discount_amount" field. +func DiscountAmountNotIn(vs ...decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldDiscountAmount, vs...)) +} + +// DiscountAmountGT applies the GT predicate on the "discount_amount" field. +func DiscountAmountGT(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldDiscountAmount, v)) +} + +// DiscountAmountGTE applies the GTE predicate on the "discount_amount" field. +func DiscountAmountGTE(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldDiscountAmount, v)) +} + +// DiscountAmountLT applies the LT predicate on the "discount_amount" field. +func DiscountAmountLT(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldDiscountAmount, v)) +} + +// DiscountAmountLTE applies the LTE predicate on the "discount_amount" field. +func DiscountAmountLTE(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldDiscountAmount, v)) +} + +// TaxAmountEQ applies the EQ predicate on the "tax_amount" field. +func TaxAmountEQ(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldTaxAmount, v)) +} + +// TaxAmountNEQ applies the NEQ predicate on the "tax_amount" field. +func TaxAmountNEQ(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldTaxAmount, v)) +} + +// TaxAmountIn applies the In predicate on the "tax_amount" field. +func TaxAmountIn(vs ...decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldTaxAmount, vs...)) +} + +// TaxAmountNotIn applies the NotIn predicate on the "tax_amount" field. +func TaxAmountNotIn(vs ...decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldTaxAmount, vs...)) +} + +// TaxAmountGT applies the GT predicate on the "tax_amount" field. +func TaxAmountGT(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldTaxAmount, v)) +} + +// TaxAmountGTE applies the GTE predicate on the "tax_amount" field. +func TaxAmountGTE(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldTaxAmount, v)) +} + +// TaxAmountLT applies the LT predicate on the "tax_amount" field. +func TaxAmountLT(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldTaxAmount, v)) +} + +// TaxAmountLTE applies the LTE predicate on the "tax_amount" field. +func TaxAmountLTE(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldTaxAmount, v)) +} + +// TotalEQ applies the EQ predicate on the "total" field. +func TotalEQ(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldTotal, v)) +} + +// TotalNEQ applies the NEQ predicate on the "total" field. +func TotalNEQ(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldTotal, v)) +} + +// TotalIn applies the In predicate on the "total" field. +func TotalIn(vs ...decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldTotal, vs...)) +} + +// TotalNotIn applies the NotIn predicate on the "total" field. +func TotalNotIn(vs ...decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldTotal, vs...)) +} + +// TotalGT applies the GT predicate on the "total" field. +func TotalGT(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldTotal, v)) +} + +// TotalGTE applies the GTE predicate on the "total" field. +func TotalGTE(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldTotal, v)) +} + +// TotalLT applies the LT predicate on the "total" field. +func TotalLT(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldTotal, v)) +} + +// TotalLTE applies the LTE predicate on the "total" field. +func TotalLTE(v decimal.Decimal) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldTotal, v)) +} + +// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. +func ExpiresAtEQ(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldEQ(FieldExpiresAt, v)) +} + +// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. +func ExpiresAtNEQ(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldNEQ(FieldExpiresAt, v)) +} + +// ExpiresAtIn applies the In predicate on the "expires_at" field. +func ExpiresAtIn(vs ...time.Time) predicate.Cart { + return predicate.Cart(sql.FieldIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. +func ExpiresAtNotIn(vs ...time.Time) predicate.Cart { + return predicate.Cart(sql.FieldNotIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtGT applies the GT predicate on the "expires_at" field. +func ExpiresAtGT(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldGT(FieldExpiresAt, v)) +} + +// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. +func ExpiresAtGTE(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldGTE(FieldExpiresAt, v)) +} + +// ExpiresAtLT applies the LT predicate on the "expires_at" field. +func ExpiresAtLT(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldLT(FieldExpiresAt, v)) +} + +// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. +func ExpiresAtLTE(v time.Time) predicate.Cart { + return predicate.Cart(sql.FieldLTE(FieldExpiresAt, v)) +} + +// HasLineItems applies the HasEdge predicate on the "line_items" edge. +func HasLineItems() predicate.Cart { + return predicate.Cart(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, LineItemsTable, LineItemsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLineItemsWith applies the HasEdge predicate on the "line_items" edge with a given conditions (other predicates). +func HasLineItemsWith(preds ...predicate.CartLineItems) predicate.Cart { + return predicate.Cart(func(s *sql.Selector) { + step := newLineItemsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasUser applies the HasEdge predicate on the "user" edge. +func HasUser() predicate.Cart { + return predicate.Cart(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates). +func HasUserWith(preds ...predicate.User) predicate.Cart { + return predicate.Cart(func(s *sql.Selector) { + step := newUserStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Cart) predicate.Cart { + return predicate.Cart(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Cart) predicate.Cart { + return predicate.Cart(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Cart) predicate.Cart { + return predicate.Cart(sql.NotPredicates(p)) +} diff --git a/ent/cart_create.go b/ent/cart_create.go new file mode 100644 index 0000000..541fdb5 --- /dev/null +++ b/ent/cart_create.go @@ -0,0 +1,529 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" + "github.com/omkar273/codegeeky/ent/user" + "github.com/shopspring/decimal" +) + +// CartCreate is the builder for creating a Cart entity. +type CartCreate struct { + config + mutation *CartMutation + hooks []Hook +} + +// SetStatus sets the "status" field. +func (cc *CartCreate) SetStatus(s string) *CartCreate { + cc.mutation.SetStatus(s) + return cc +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (cc *CartCreate) SetNillableStatus(s *string) *CartCreate { + if s != nil { + cc.SetStatus(*s) + } + return cc +} + +// SetCreatedAt sets the "created_at" field. +func (cc *CartCreate) SetCreatedAt(t time.Time) *CartCreate { + cc.mutation.SetCreatedAt(t) + return cc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (cc *CartCreate) SetNillableCreatedAt(t *time.Time) *CartCreate { + if t != nil { + cc.SetCreatedAt(*t) + } + return cc +} + +// SetUpdatedAt sets the "updated_at" field. +func (cc *CartCreate) SetUpdatedAt(t time.Time) *CartCreate { + cc.mutation.SetUpdatedAt(t) + return cc +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (cc *CartCreate) SetNillableUpdatedAt(t *time.Time) *CartCreate { + if t != nil { + cc.SetUpdatedAt(*t) + } + return cc +} + +// SetCreatedBy sets the "created_by" field. +func (cc *CartCreate) SetCreatedBy(s string) *CartCreate { + cc.mutation.SetCreatedBy(s) + return cc +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (cc *CartCreate) SetNillableCreatedBy(s *string) *CartCreate { + if s != nil { + cc.SetCreatedBy(*s) + } + return cc +} + +// SetUpdatedBy sets the "updated_by" field. +func (cc *CartCreate) SetUpdatedBy(s string) *CartCreate { + cc.mutation.SetUpdatedBy(s) + return cc +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (cc *CartCreate) SetNillableUpdatedBy(s *string) *CartCreate { + if s != nil { + cc.SetUpdatedBy(*s) + } + return cc +} + +// SetMetadata sets the "metadata" field. +func (cc *CartCreate) SetMetadata(m map[string]string) *CartCreate { + cc.mutation.SetMetadata(m) + return cc +} + +// SetUserID sets the "user_id" field. +func (cc *CartCreate) SetUserID(s string) *CartCreate { + cc.mutation.SetUserID(s) + return cc +} + +// SetType sets the "type" field. +func (cc *CartCreate) SetType(s string) *CartCreate { + cc.mutation.SetType(s) + return cc +} + +// SetSubtotal sets the "subtotal" field. +func (cc *CartCreate) SetSubtotal(d decimal.Decimal) *CartCreate { + cc.mutation.SetSubtotal(d) + return cc +} + +// SetNillableSubtotal sets the "subtotal" field if the given value is not nil. +func (cc *CartCreate) SetNillableSubtotal(d *decimal.Decimal) *CartCreate { + if d != nil { + cc.SetSubtotal(*d) + } + return cc +} + +// SetDiscountAmount sets the "discount_amount" field. +func (cc *CartCreate) SetDiscountAmount(d decimal.Decimal) *CartCreate { + cc.mutation.SetDiscountAmount(d) + return cc +} + +// SetNillableDiscountAmount sets the "discount_amount" field if the given value is not nil. +func (cc *CartCreate) SetNillableDiscountAmount(d *decimal.Decimal) *CartCreate { + if d != nil { + cc.SetDiscountAmount(*d) + } + return cc +} + +// SetTaxAmount sets the "tax_amount" field. +func (cc *CartCreate) SetTaxAmount(d decimal.Decimal) *CartCreate { + cc.mutation.SetTaxAmount(d) + return cc +} + +// SetNillableTaxAmount sets the "tax_amount" field if the given value is not nil. +func (cc *CartCreate) SetNillableTaxAmount(d *decimal.Decimal) *CartCreate { + if d != nil { + cc.SetTaxAmount(*d) + } + return cc +} + +// SetTotal sets the "total" field. +func (cc *CartCreate) SetTotal(d decimal.Decimal) *CartCreate { + cc.mutation.SetTotal(d) + return cc +} + +// SetNillableTotal sets the "total" field if the given value is not nil. +func (cc *CartCreate) SetNillableTotal(d *decimal.Decimal) *CartCreate { + if d != nil { + cc.SetTotal(*d) + } + return cc +} + +// SetExpiresAt sets the "expires_at" field. +func (cc *CartCreate) SetExpiresAt(t time.Time) *CartCreate { + cc.mutation.SetExpiresAt(t) + return cc +} + +// SetID sets the "id" field. +func (cc *CartCreate) SetID(s string) *CartCreate { + cc.mutation.SetID(s) + return cc +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (cc *CartCreate) SetNillableID(s *string) *CartCreate { + if s != nil { + cc.SetID(*s) + } + return cc +} + +// AddLineItemIDs adds the "line_items" edge to the CartLineItems entity by IDs. +func (cc *CartCreate) AddLineItemIDs(ids ...string) *CartCreate { + cc.mutation.AddLineItemIDs(ids...) + return cc +} + +// AddLineItems adds the "line_items" edges to the CartLineItems entity. +func (cc *CartCreate) AddLineItems(c ...*CartLineItems) *CartCreate { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return cc.AddLineItemIDs(ids...) +} + +// SetUser sets the "user" edge to the User entity. +func (cc *CartCreate) SetUser(u *User) *CartCreate { + return cc.SetUserID(u.ID) +} + +// Mutation returns the CartMutation object of the builder. +func (cc *CartCreate) Mutation() *CartMutation { + return cc.mutation +} + +// Save creates the Cart in the database. +func (cc *CartCreate) Save(ctx context.Context) (*Cart, error) { + cc.defaults() + return withHooks(ctx, cc.sqlSave, cc.mutation, cc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (cc *CartCreate) SaveX(ctx context.Context) *Cart { + v, err := cc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (cc *CartCreate) Exec(ctx context.Context) error { + _, err := cc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (cc *CartCreate) ExecX(ctx context.Context) { + if err := cc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (cc *CartCreate) defaults() { + if _, ok := cc.mutation.Status(); !ok { + v := cart.DefaultStatus + cc.mutation.SetStatus(v) + } + if _, ok := cc.mutation.CreatedAt(); !ok { + v := cart.DefaultCreatedAt() + cc.mutation.SetCreatedAt(v) + } + if _, ok := cc.mutation.UpdatedAt(); !ok { + v := cart.DefaultUpdatedAt() + cc.mutation.SetUpdatedAt(v) + } + if _, ok := cc.mutation.Metadata(); !ok { + v := cart.DefaultMetadata + cc.mutation.SetMetadata(v) + } + if _, ok := cc.mutation.Subtotal(); !ok { + v := cart.DefaultSubtotal + cc.mutation.SetSubtotal(v) + } + if _, ok := cc.mutation.DiscountAmount(); !ok { + v := cart.DefaultDiscountAmount + cc.mutation.SetDiscountAmount(v) + } + if _, ok := cc.mutation.TaxAmount(); !ok { + v := cart.DefaultTaxAmount + cc.mutation.SetTaxAmount(v) + } + if _, ok := cc.mutation.Total(); !ok { + v := cart.DefaultTotal + cc.mutation.SetTotal(v) + } + if _, ok := cc.mutation.ID(); !ok { + v := cart.DefaultID() + cc.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (cc *CartCreate) check() error { + if _, ok := cc.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "Cart.status"`)} + } + if _, ok := cc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Cart.created_at"`)} + } + if _, ok := cc.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "Cart.updated_at"`)} + } + if _, ok := cc.mutation.UserID(); !ok { + return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "Cart.user_id"`)} + } + if v, ok := cc.mutation.UserID(); ok { + if err := cart.UserIDValidator(v); err != nil { + return &ValidationError{Name: "user_id", err: fmt.Errorf(`ent: validator failed for field "Cart.user_id": %w`, err)} + } + } + if _, ok := cc.mutation.GetType(); !ok { + return &ValidationError{Name: "type", err: errors.New(`ent: missing required field "Cart.type"`)} + } + if v, ok := cc.mutation.GetType(); ok { + if err := cart.TypeValidator(v); err != nil { + return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "Cart.type": %w`, err)} + } + } + if _, ok := cc.mutation.Subtotal(); !ok { + return &ValidationError{Name: "subtotal", err: errors.New(`ent: missing required field "Cart.subtotal"`)} + } + if _, ok := cc.mutation.DiscountAmount(); !ok { + return &ValidationError{Name: "discount_amount", err: errors.New(`ent: missing required field "Cart.discount_amount"`)} + } + if _, ok := cc.mutation.TaxAmount(); !ok { + return &ValidationError{Name: "tax_amount", err: errors.New(`ent: missing required field "Cart.tax_amount"`)} + } + if _, ok := cc.mutation.Total(); !ok { + return &ValidationError{Name: "total", err: errors.New(`ent: missing required field "Cart.total"`)} + } + if _, ok := cc.mutation.ExpiresAt(); !ok { + return &ValidationError{Name: "expires_at", err: errors.New(`ent: missing required field "Cart.expires_at"`)} + } + if len(cc.mutation.UserIDs()) == 0 { + return &ValidationError{Name: "user", err: errors.New(`ent: missing required edge "Cart.user"`)} + } + return nil +} + +func (cc *CartCreate) sqlSave(ctx context.Context) (*Cart, error) { + if err := cc.check(); err != nil { + return nil, err + } + _node, _spec := cc.createSpec() + if err := sqlgraph.CreateNode(ctx, cc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(string); ok { + _node.ID = id + } else { + return nil, fmt.Errorf("unexpected Cart.ID type: %T", _spec.ID.Value) + } + } + cc.mutation.id = &_node.ID + cc.mutation.done = true + return _node, nil +} + +func (cc *CartCreate) createSpec() (*Cart, *sqlgraph.CreateSpec) { + var ( + _node = &Cart{config: cc.config} + _spec = sqlgraph.NewCreateSpec(cart.Table, sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString)) + ) + if id, ok := cc.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := cc.mutation.Status(); ok { + _spec.SetField(cart.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := cc.mutation.CreatedAt(); ok { + _spec.SetField(cart.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := cc.mutation.UpdatedAt(); ok { + _spec.SetField(cart.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := cc.mutation.CreatedBy(); ok { + _spec.SetField(cart.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := cc.mutation.UpdatedBy(); ok { + _spec.SetField(cart.FieldUpdatedBy, field.TypeString, value) + _node.UpdatedBy = value + } + if value, ok := cc.mutation.Metadata(); ok { + _spec.SetField(cart.FieldMetadata, field.TypeJSON, value) + _node.Metadata = value + } + if value, ok := cc.mutation.GetType(); ok { + _spec.SetField(cart.FieldType, field.TypeString, value) + _node.Type = value + } + if value, ok := cc.mutation.Subtotal(); ok { + _spec.SetField(cart.FieldSubtotal, field.TypeOther, value) + _node.Subtotal = value + } + if value, ok := cc.mutation.DiscountAmount(); ok { + _spec.SetField(cart.FieldDiscountAmount, field.TypeOther, value) + _node.DiscountAmount = value + } + if value, ok := cc.mutation.TaxAmount(); ok { + _spec.SetField(cart.FieldTaxAmount, field.TypeOther, value) + _node.TaxAmount = value + } + if value, ok := cc.mutation.Total(); ok { + _spec.SetField(cart.FieldTotal, field.TypeOther, value) + _node.Total = value + } + if value, ok := cc.mutation.ExpiresAt(); ok { + _spec.SetField(cart.FieldExpiresAt, field.TypeTime, value) + _node.ExpiresAt = value + } + if nodes := cc.mutation.LineItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: cart.LineItemsTable, + Columns: []string{cart.LineItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := cc.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: cart.UserTable, + Columns: []string{cart.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.UserID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// CartCreateBulk is the builder for creating many Cart entities in bulk. +type CartCreateBulk struct { + config + err error + builders []*CartCreate +} + +// Save creates the Cart entities in the database. +func (ccb *CartCreateBulk) Save(ctx context.Context) ([]*Cart, error) { + if ccb.err != nil { + return nil, ccb.err + } + specs := make([]*sqlgraph.CreateSpec, len(ccb.builders)) + nodes := make([]*Cart, len(ccb.builders)) + mutators := make([]Mutator, len(ccb.builders)) + for i := range ccb.builders { + func(i int, root context.Context) { + builder := ccb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*CartMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, ccb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, ccb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, ccb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (ccb *CartCreateBulk) SaveX(ctx context.Context) []*Cart { + v, err := ccb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (ccb *CartCreateBulk) Exec(ctx context.Context) error { + _, err := ccb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ccb *CartCreateBulk) ExecX(ctx context.Context) { + if err := ccb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/cart_delete.go b/ent/cart_delete.go new file mode 100644 index 0000000..167c748 --- /dev/null +++ b/ent/cart_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// CartDelete is the builder for deleting a Cart entity. +type CartDelete struct { + config + hooks []Hook + mutation *CartMutation +} + +// Where appends a list predicates to the CartDelete builder. +func (cd *CartDelete) Where(ps ...predicate.Cart) *CartDelete { + cd.mutation.Where(ps...) + return cd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (cd *CartDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, cd.sqlExec, cd.mutation, cd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (cd *CartDelete) ExecX(ctx context.Context) int { + n, err := cd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (cd *CartDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(cart.Table, sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString)) + if ps := cd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, cd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + cd.mutation.done = true + return affected, err +} + +// CartDeleteOne is the builder for deleting a single Cart entity. +type CartDeleteOne struct { + cd *CartDelete +} + +// Where appends a list predicates to the CartDelete builder. +func (cdo *CartDeleteOne) Where(ps ...predicate.Cart) *CartDeleteOne { + cdo.cd.mutation.Where(ps...) + return cdo +} + +// Exec executes the deletion query. +func (cdo *CartDeleteOne) Exec(ctx context.Context) error { + n, err := cdo.cd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{cart.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (cdo *CartDeleteOne) ExecX(ctx context.Context) { + if err := cdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/cart_query.go b/ent/cart_query.go new file mode 100644 index 0000000..a91ec50 --- /dev/null +++ b/ent/cart_query.go @@ -0,0 +1,681 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" + "github.com/omkar273/codegeeky/ent/predicate" + "github.com/omkar273/codegeeky/ent/user" +) + +// CartQuery is the builder for querying Cart entities. +type CartQuery struct { + config + ctx *QueryContext + order []cart.OrderOption + inters []Interceptor + predicates []predicate.Cart + withLineItems *CartLineItemsQuery + withUser *UserQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the CartQuery builder. +func (cq *CartQuery) Where(ps ...predicate.Cart) *CartQuery { + cq.predicates = append(cq.predicates, ps...) + return cq +} + +// Limit the number of records to be returned by this query. +func (cq *CartQuery) Limit(limit int) *CartQuery { + cq.ctx.Limit = &limit + return cq +} + +// Offset to start from. +func (cq *CartQuery) Offset(offset int) *CartQuery { + cq.ctx.Offset = &offset + return cq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (cq *CartQuery) Unique(unique bool) *CartQuery { + cq.ctx.Unique = &unique + return cq +} + +// Order specifies how the records should be ordered. +func (cq *CartQuery) Order(o ...cart.OrderOption) *CartQuery { + cq.order = append(cq.order, o...) + return cq +} + +// QueryLineItems chains the current query on the "line_items" edge. +func (cq *CartQuery) QueryLineItems() *CartLineItemsQuery { + query := (&CartLineItemsClient{config: cq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := cq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := cq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(cart.Table, cart.FieldID, selector), + sqlgraph.To(cartlineitems.Table, cartlineitems.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, cart.LineItemsTable, cart.LineItemsColumn), + ) + fromU = sqlgraph.SetNeighbors(cq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryUser chains the current query on the "user" edge. +func (cq *CartQuery) QueryUser() *UserQuery { + query := (&UserClient{config: cq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := cq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := cq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(cart.Table, cart.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, cart.UserTable, cart.UserColumn), + ) + fromU = sqlgraph.SetNeighbors(cq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Cart entity from the query. +// Returns a *NotFoundError when no Cart was found. +func (cq *CartQuery) First(ctx context.Context) (*Cart, error) { + nodes, err := cq.Limit(1).All(setContextOp(ctx, cq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{cart.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (cq *CartQuery) FirstX(ctx context.Context) *Cart { + node, err := cq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Cart ID from the query. +// Returns a *NotFoundError when no Cart ID was found. +func (cq *CartQuery) FirstID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = cq.Limit(1).IDs(setContextOp(ctx, cq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{cart.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (cq *CartQuery) FirstIDX(ctx context.Context) string { + id, err := cq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Cart entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Cart entity is found. +// Returns a *NotFoundError when no Cart entities are found. +func (cq *CartQuery) Only(ctx context.Context) (*Cart, error) { + nodes, err := cq.Limit(2).All(setContextOp(ctx, cq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{cart.Label} + default: + return nil, &NotSingularError{cart.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (cq *CartQuery) OnlyX(ctx context.Context) *Cart { + node, err := cq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Cart ID in the query. +// Returns a *NotSingularError when more than one Cart ID is found. +// Returns a *NotFoundError when no entities are found. +func (cq *CartQuery) OnlyID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = cq.Limit(2).IDs(setContextOp(ctx, cq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{cart.Label} + default: + err = &NotSingularError{cart.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (cq *CartQuery) OnlyIDX(ctx context.Context) string { + id, err := cq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Carts. +func (cq *CartQuery) All(ctx context.Context) ([]*Cart, error) { + ctx = setContextOp(ctx, cq.ctx, ent.OpQueryAll) + if err := cq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Cart, *CartQuery]() + return withInterceptors[[]*Cart](ctx, cq, qr, cq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (cq *CartQuery) AllX(ctx context.Context) []*Cart { + nodes, err := cq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Cart IDs. +func (cq *CartQuery) IDs(ctx context.Context) (ids []string, err error) { + if cq.ctx.Unique == nil && cq.path != nil { + cq.Unique(true) + } + ctx = setContextOp(ctx, cq.ctx, ent.OpQueryIDs) + if err = cq.Select(cart.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (cq *CartQuery) IDsX(ctx context.Context) []string { + ids, err := cq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (cq *CartQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, cq.ctx, ent.OpQueryCount) + if err := cq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, cq, querierCount[*CartQuery](), cq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (cq *CartQuery) CountX(ctx context.Context) int { + count, err := cq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (cq *CartQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, cq.ctx, ent.OpQueryExist) + switch _, err := cq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (cq *CartQuery) ExistX(ctx context.Context) bool { + exist, err := cq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the CartQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (cq *CartQuery) Clone() *CartQuery { + if cq == nil { + return nil + } + return &CartQuery{ + config: cq.config, + ctx: cq.ctx.Clone(), + order: append([]cart.OrderOption{}, cq.order...), + inters: append([]Interceptor{}, cq.inters...), + predicates: append([]predicate.Cart{}, cq.predicates...), + withLineItems: cq.withLineItems.Clone(), + withUser: cq.withUser.Clone(), + // clone intermediate query. + sql: cq.sql.Clone(), + path: cq.path, + } +} + +// WithLineItems tells the query-builder to eager-load the nodes that are connected to +// the "line_items" edge. The optional arguments are used to configure the query builder of the edge. +func (cq *CartQuery) WithLineItems(opts ...func(*CartLineItemsQuery)) *CartQuery { + query := (&CartLineItemsClient{config: cq.config}).Query() + for _, opt := range opts { + opt(query) + } + cq.withLineItems = query + return cq +} + +// WithUser tells the query-builder to eager-load the nodes that are connected to +// the "user" edge. The optional arguments are used to configure the query builder of the edge. +func (cq *CartQuery) WithUser(opts ...func(*UserQuery)) *CartQuery { + query := (&UserClient{config: cq.config}).Query() + for _, opt := range opts { + opt(query) + } + cq.withUser = query + return cq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Status string `json:"status,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Cart.Query(). +// GroupBy(cart.FieldStatus). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (cq *CartQuery) GroupBy(field string, fields ...string) *CartGroupBy { + cq.ctx.Fields = append([]string{field}, fields...) + grbuild := &CartGroupBy{build: cq} + grbuild.flds = &cq.ctx.Fields + grbuild.label = cart.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Status string `json:"status,omitempty"` +// } +// +// client.Cart.Query(). +// Select(cart.FieldStatus). +// Scan(ctx, &v) +func (cq *CartQuery) Select(fields ...string) *CartSelect { + cq.ctx.Fields = append(cq.ctx.Fields, fields...) + sbuild := &CartSelect{CartQuery: cq} + sbuild.label = cart.Label + sbuild.flds, sbuild.scan = &cq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a CartSelect configured with the given aggregations. +func (cq *CartQuery) Aggregate(fns ...AggregateFunc) *CartSelect { + return cq.Select().Aggregate(fns...) +} + +func (cq *CartQuery) prepareQuery(ctx context.Context) error { + for _, inter := range cq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, cq); err != nil { + return err + } + } + } + for _, f := range cq.ctx.Fields { + if !cart.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if cq.path != nil { + prev, err := cq.path(ctx) + if err != nil { + return err + } + cq.sql = prev + } + return nil +} + +func (cq *CartQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Cart, error) { + var ( + nodes = []*Cart{} + _spec = cq.querySpec() + loadedTypes = [2]bool{ + cq.withLineItems != nil, + cq.withUser != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Cart).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Cart{config: cq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, cq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := cq.withLineItems; query != nil { + if err := cq.loadLineItems(ctx, query, nodes, + func(n *Cart) { n.Edges.LineItems = []*CartLineItems{} }, + func(n *Cart, e *CartLineItems) { n.Edges.LineItems = append(n.Edges.LineItems, e) }); err != nil { + return nil, err + } + } + if query := cq.withUser; query != nil { + if err := cq.loadUser(ctx, query, nodes, nil, + func(n *Cart, e *User) { n.Edges.User = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (cq *CartQuery) loadLineItems(ctx context.Context, query *CartLineItemsQuery, nodes []*Cart, init func(*Cart), assign func(*Cart, *CartLineItems)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*Cart) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(cartlineitems.FieldCartID) + } + query.Where(predicate.CartLineItems(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(cart.LineItemsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.CartID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "cart_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} +func (cq *CartQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*Cart, init func(*Cart), assign func(*Cart, *User)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*Cart) + for i := range nodes { + fk := nodes[i].UserID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "user_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (cq *CartQuery) sqlCount(ctx context.Context) (int, error) { + _spec := cq.querySpec() + _spec.Node.Columns = cq.ctx.Fields + if len(cq.ctx.Fields) > 0 { + _spec.Unique = cq.ctx.Unique != nil && *cq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, cq.driver, _spec) +} + +func (cq *CartQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(cart.Table, cart.Columns, sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString)) + _spec.From = cq.sql + if unique := cq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if cq.path != nil { + _spec.Unique = true + } + if fields := cq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, cart.FieldID) + for i := range fields { + if fields[i] != cart.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if cq.withUser != nil { + _spec.Node.AddColumnOnce(cart.FieldUserID) + } + } + if ps := cq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := cq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := cq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := cq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (cq *CartQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(cq.driver.Dialect()) + t1 := builder.Table(cart.Table) + columns := cq.ctx.Fields + if len(columns) == 0 { + columns = cart.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if cq.sql != nil { + selector = cq.sql + selector.Select(selector.Columns(columns...)...) + } + if cq.ctx.Unique != nil && *cq.ctx.Unique { + selector.Distinct() + } + for _, p := range cq.predicates { + p(selector) + } + for _, p := range cq.order { + p(selector) + } + if offset := cq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := cq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// CartGroupBy is the group-by builder for Cart entities. +type CartGroupBy struct { + selector + build *CartQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (cgb *CartGroupBy) Aggregate(fns ...AggregateFunc) *CartGroupBy { + cgb.fns = append(cgb.fns, fns...) + return cgb +} + +// Scan applies the selector query and scans the result into the given value. +func (cgb *CartGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, cgb.build.ctx, ent.OpQueryGroupBy) + if err := cgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*CartQuery, *CartGroupBy](ctx, cgb.build, cgb, cgb.build.inters, v) +} + +func (cgb *CartGroupBy) sqlScan(ctx context.Context, root *CartQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(cgb.fns)) + for _, fn := range cgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*cgb.flds)+len(cgb.fns)) + for _, f := range *cgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*cgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := cgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// CartSelect is the builder for selecting fields of Cart entities. +type CartSelect struct { + *CartQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (cs *CartSelect) Aggregate(fns ...AggregateFunc) *CartSelect { + cs.fns = append(cs.fns, fns...) + return cs +} + +// Scan applies the selector query and scans the result into the given value. +func (cs *CartSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, cs.ctx, ent.OpQuerySelect) + if err := cs.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*CartQuery, *CartSelect](ctx, cs.CartQuery, cs, cs.inters, v) +} + +func (cs *CartSelect) sqlScan(ctx context.Context, root *CartQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(cs.fns)) + for _, fn := range cs.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*cs.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := cs.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/cart_update.go b/ent/cart_update.go new file mode 100644 index 0000000..46d8cc3 --- /dev/null +++ b/ent/cart_update.go @@ -0,0 +1,525 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// CartUpdate is the builder for updating Cart entities. +type CartUpdate struct { + config + hooks []Hook + mutation *CartMutation +} + +// Where appends a list predicates to the CartUpdate builder. +func (cu *CartUpdate) Where(ps ...predicate.Cart) *CartUpdate { + cu.mutation.Where(ps...) + return cu +} + +// SetStatus sets the "status" field. +func (cu *CartUpdate) SetStatus(s string) *CartUpdate { + cu.mutation.SetStatus(s) + return cu +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (cu *CartUpdate) SetNillableStatus(s *string) *CartUpdate { + if s != nil { + cu.SetStatus(*s) + } + return cu +} + +// SetUpdatedAt sets the "updated_at" field. +func (cu *CartUpdate) SetUpdatedAt(t time.Time) *CartUpdate { + cu.mutation.SetUpdatedAt(t) + return cu +} + +// SetUpdatedBy sets the "updated_by" field. +func (cu *CartUpdate) SetUpdatedBy(s string) *CartUpdate { + cu.mutation.SetUpdatedBy(s) + return cu +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (cu *CartUpdate) SetNillableUpdatedBy(s *string) *CartUpdate { + if s != nil { + cu.SetUpdatedBy(*s) + } + return cu +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (cu *CartUpdate) ClearUpdatedBy() *CartUpdate { + cu.mutation.ClearUpdatedBy() + return cu +} + +// SetMetadata sets the "metadata" field. +func (cu *CartUpdate) SetMetadata(m map[string]string) *CartUpdate { + cu.mutation.SetMetadata(m) + return cu +} + +// ClearMetadata clears the value of the "metadata" field. +func (cu *CartUpdate) ClearMetadata() *CartUpdate { + cu.mutation.ClearMetadata() + return cu +} + +// AddLineItemIDs adds the "line_items" edge to the CartLineItems entity by IDs. +func (cu *CartUpdate) AddLineItemIDs(ids ...string) *CartUpdate { + cu.mutation.AddLineItemIDs(ids...) + return cu +} + +// AddLineItems adds the "line_items" edges to the CartLineItems entity. +func (cu *CartUpdate) AddLineItems(c ...*CartLineItems) *CartUpdate { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return cu.AddLineItemIDs(ids...) +} + +// Mutation returns the CartMutation object of the builder. +func (cu *CartUpdate) Mutation() *CartMutation { + return cu.mutation +} + +// ClearLineItems clears all "line_items" edges to the CartLineItems entity. +func (cu *CartUpdate) ClearLineItems() *CartUpdate { + cu.mutation.ClearLineItems() + return cu +} + +// RemoveLineItemIDs removes the "line_items" edge to CartLineItems entities by IDs. +func (cu *CartUpdate) RemoveLineItemIDs(ids ...string) *CartUpdate { + cu.mutation.RemoveLineItemIDs(ids...) + return cu +} + +// RemoveLineItems removes "line_items" edges to CartLineItems entities. +func (cu *CartUpdate) RemoveLineItems(c ...*CartLineItems) *CartUpdate { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return cu.RemoveLineItemIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (cu *CartUpdate) Save(ctx context.Context) (int, error) { + cu.defaults() + return withHooks(ctx, cu.sqlSave, cu.mutation, cu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (cu *CartUpdate) SaveX(ctx context.Context) int { + affected, err := cu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (cu *CartUpdate) Exec(ctx context.Context) error { + _, err := cu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (cu *CartUpdate) ExecX(ctx context.Context) { + if err := cu.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (cu *CartUpdate) defaults() { + if _, ok := cu.mutation.UpdatedAt(); !ok { + v := cart.UpdateDefaultUpdatedAt() + cu.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (cu *CartUpdate) check() error { + if cu.mutation.UserCleared() && len(cu.mutation.UserIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Cart.user"`) + } + return nil +} + +func (cu *CartUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := cu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(cart.Table, cart.Columns, sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString)) + if ps := cu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := cu.mutation.Status(); ok { + _spec.SetField(cart.FieldStatus, field.TypeString, value) + } + if value, ok := cu.mutation.UpdatedAt(); ok { + _spec.SetField(cart.FieldUpdatedAt, field.TypeTime, value) + } + if cu.mutation.CreatedByCleared() { + _spec.ClearField(cart.FieldCreatedBy, field.TypeString) + } + if value, ok := cu.mutation.UpdatedBy(); ok { + _spec.SetField(cart.FieldUpdatedBy, field.TypeString, value) + } + if cu.mutation.UpdatedByCleared() { + _spec.ClearField(cart.FieldUpdatedBy, field.TypeString) + } + if value, ok := cu.mutation.Metadata(); ok { + _spec.SetField(cart.FieldMetadata, field.TypeJSON, value) + } + if cu.mutation.MetadataCleared() { + _spec.ClearField(cart.FieldMetadata, field.TypeJSON) + } + if cu.mutation.LineItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: cart.LineItemsTable, + Columns: []string{cart.LineItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := cu.mutation.RemovedLineItemsIDs(); len(nodes) > 0 && !cu.mutation.LineItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: cart.LineItemsTable, + Columns: []string{cart.LineItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := cu.mutation.LineItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: cart.LineItemsTable, + Columns: []string{cart.LineItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if n, err = sqlgraph.UpdateNodes(ctx, cu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{cart.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + cu.mutation.done = true + return n, nil +} + +// CartUpdateOne is the builder for updating a single Cart entity. +type CartUpdateOne struct { + config + fields []string + hooks []Hook + mutation *CartMutation +} + +// SetStatus sets the "status" field. +func (cuo *CartUpdateOne) SetStatus(s string) *CartUpdateOne { + cuo.mutation.SetStatus(s) + return cuo +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (cuo *CartUpdateOne) SetNillableStatus(s *string) *CartUpdateOne { + if s != nil { + cuo.SetStatus(*s) + } + return cuo +} + +// SetUpdatedAt sets the "updated_at" field. +func (cuo *CartUpdateOne) SetUpdatedAt(t time.Time) *CartUpdateOne { + cuo.mutation.SetUpdatedAt(t) + return cuo +} + +// SetUpdatedBy sets the "updated_by" field. +func (cuo *CartUpdateOne) SetUpdatedBy(s string) *CartUpdateOne { + cuo.mutation.SetUpdatedBy(s) + return cuo +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (cuo *CartUpdateOne) SetNillableUpdatedBy(s *string) *CartUpdateOne { + if s != nil { + cuo.SetUpdatedBy(*s) + } + return cuo +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (cuo *CartUpdateOne) ClearUpdatedBy() *CartUpdateOne { + cuo.mutation.ClearUpdatedBy() + return cuo +} + +// SetMetadata sets the "metadata" field. +func (cuo *CartUpdateOne) SetMetadata(m map[string]string) *CartUpdateOne { + cuo.mutation.SetMetadata(m) + return cuo +} + +// ClearMetadata clears the value of the "metadata" field. +func (cuo *CartUpdateOne) ClearMetadata() *CartUpdateOne { + cuo.mutation.ClearMetadata() + return cuo +} + +// AddLineItemIDs adds the "line_items" edge to the CartLineItems entity by IDs. +func (cuo *CartUpdateOne) AddLineItemIDs(ids ...string) *CartUpdateOne { + cuo.mutation.AddLineItemIDs(ids...) + return cuo +} + +// AddLineItems adds the "line_items" edges to the CartLineItems entity. +func (cuo *CartUpdateOne) AddLineItems(c ...*CartLineItems) *CartUpdateOne { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return cuo.AddLineItemIDs(ids...) +} + +// Mutation returns the CartMutation object of the builder. +func (cuo *CartUpdateOne) Mutation() *CartMutation { + return cuo.mutation +} + +// ClearLineItems clears all "line_items" edges to the CartLineItems entity. +func (cuo *CartUpdateOne) ClearLineItems() *CartUpdateOne { + cuo.mutation.ClearLineItems() + return cuo +} + +// RemoveLineItemIDs removes the "line_items" edge to CartLineItems entities by IDs. +func (cuo *CartUpdateOne) RemoveLineItemIDs(ids ...string) *CartUpdateOne { + cuo.mutation.RemoveLineItemIDs(ids...) + return cuo +} + +// RemoveLineItems removes "line_items" edges to CartLineItems entities. +func (cuo *CartUpdateOne) RemoveLineItems(c ...*CartLineItems) *CartUpdateOne { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return cuo.RemoveLineItemIDs(ids...) +} + +// Where appends a list predicates to the CartUpdate builder. +func (cuo *CartUpdateOne) Where(ps ...predicate.Cart) *CartUpdateOne { + cuo.mutation.Where(ps...) + return cuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (cuo *CartUpdateOne) Select(field string, fields ...string) *CartUpdateOne { + cuo.fields = append([]string{field}, fields...) + return cuo +} + +// Save executes the query and returns the updated Cart entity. +func (cuo *CartUpdateOne) Save(ctx context.Context) (*Cart, error) { + cuo.defaults() + return withHooks(ctx, cuo.sqlSave, cuo.mutation, cuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (cuo *CartUpdateOne) SaveX(ctx context.Context) *Cart { + node, err := cuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (cuo *CartUpdateOne) Exec(ctx context.Context) error { + _, err := cuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (cuo *CartUpdateOne) ExecX(ctx context.Context) { + if err := cuo.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (cuo *CartUpdateOne) defaults() { + if _, ok := cuo.mutation.UpdatedAt(); !ok { + v := cart.UpdateDefaultUpdatedAt() + cuo.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (cuo *CartUpdateOne) check() error { + if cuo.mutation.UserCleared() && len(cuo.mutation.UserIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Cart.user"`) + } + return nil +} + +func (cuo *CartUpdateOne) sqlSave(ctx context.Context) (_node *Cart, err error) { + if err := cuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(cart.Table, cart.Columns, sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString)) + id, ok := cuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Cart.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := cuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, cart.FieldID) + for _, f := range fields { + if !cart.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != cart.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := cuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := cuo.mutation.Status(); ok { + _spec.SetField(cart.FieldStatus, field.TypeString, value) + } + if value, ok := cuo.mutation.UpdatedAt(); ok { + _spec.SetField(cart.FieldUpdatedAt, field.TypeTime, value) + } + if cuo.mutation.CreatedByCleared() { + _spec.ClearField(cart.FieldCreatedBy, field.TypeString) + } + if value, ok := cuo.mutation.UpdatedBy(); ok { + _spec.SetField(cart.FieldUpdatedBy, field.TypeString, value) + } + if cuo.mutation.UpdatedByCleared() { + _spec.ClearField(cart.FieldUpdatedBy, field.TypeString) + } + if value, ok := cuo.mutation.Metadata(); ok { + _spec.SetField(cart.FieldMetadata, field.TypeJSON, value) + } + if cuo.mutation.MetadataCleared() { + _spec.ClearField(cart.FieldMetadata, field.TypeJSON) + } + if cuo.mutation.LineItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: cart.LineItemsTable, + Columns: []string{cart.LineItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := cuo.mutation.RemovedLineItemsIDs(); len(nodes) > 0 && !cuo.mutation.LineItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: cart.LineItemsTable, + Columns: []string{cart.LineItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := cuo.mutation.LineItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: cart.LineItemsTable, + Columns: []string{cart.LineItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Cart{config: cuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, cuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{cart.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + cuo.mutation.done = true + return _node, nil +} diff --git a/ent/cartlineitems.go b/ent/cartlineitems.go new file mode 100644 index 0000000..968e6d9 --- /dev/null +++ b/ent/cartlineitems.go @@ -0,0 +1,297 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" + "github.com/shopspring/decimal" +) + +// CartLineItems is the model entity for the CartLineItems schema. +type CartLineItems struct { + config `json:"-"` + // ID of the ent. + ID string `json:"id,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // UpdatedBy holds the value of the "updated_by" field. + UpdatedBy string `json:"updated_by,omitempty"` + // Metadata holds the value of the "metadata" field. + Metadata map[string]string `json:"metadata,omitempty"` + // CartID holds the value of the "cart_id" field. + CartID string `json:"cart_id,omitempty"` + // EntityID holds the value of the "entity_id" field. + EntityID string `json:"entity_id,omitempty"` + // EntityType holds the value of the "entity_type" field. + EntityType string `json:"entity_type,omitempty"` + // Quantity holds the value of the "quantity" field. + Quantity int `json:"quantity,omitempty"` + // PerUnitPrice holds the value of the "per_unit_price" field. + PerUnitPrice decimal.Decimal `json:"per_unit_price,omitempty"` + // TaxAmount holds the value of the "tax_amount" field. + TaxAmount decimal.Decimal `json:"tax_amount,omitempty"` + // DiscountAmount holds the value of the "discount_amount" field. + DiscountAmount decimal.Decimal `json:"discount_amount,omitempty"` + // Subtotal holds the value of the "subtotal" field. + Subtotal decimal.Decimal `json:"subtotal,omitempty"` + // Total holds the value of the "total" field. + Total decimal.Decimal `json:"total,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CartLineItemsQuery when eager-loading is set. + Edges CartLineItemsEdges `json:"edges"` + selectValues sql.SelectValues +} + +// CartLineItemsEdges holds the relations/edges for other nodes in the graph. +type CartLineItemsEdges struct { + // Cart holds the value of the cart edge. + Cart *Cart `json:"cart,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// CartOrErr returns the Cart value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e CartLineItemsEdges) CartOrErr() (*Cart, error) { + if e.Cart != nil { + return e.Cart, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: cart.Label} + } + return nil, &NotLoadedError{edge: "cart"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*CartLineItems) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case cartlineitems.FieldMetadata: + values[i] = new([]byte) + case cartlineitems.FieldPerUnitPrice, cartlineitems.FieldTaxAmount, cartlineitems.FieldDiscountAmount, cartlineitems.FieldSubtotal, cartlineitems.FieldTotal: + values[i] = new(decimal.Decimal) + case cartlineitems.FieldQuantity: + values[i] = new(sql.NullInt64) + case cartlineitems.FieldID, cartlineitems.FieldStatus, cartlineitems.FieldCreatedBy, cartlineitems.FieldUpdatedBy, cartlineitems.FieldCartID, cartlineitems.FieldEntityID, cartlineitems.FieldEntityType: + values[i] = new(sql.NullString) + case cartlineitems.FieldCreatedAt, cartlineitems.FieldUpdatedAt: + values[i] = new(sql.NullTime) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the CartLineItems fields. +func (cli *CartLineItems) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case cartlineitems.FieldID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value.Valid { + cli.ID = value.String + } + case cartlineitems.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + cli.Status = value.String + } + case cartlineitems.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + cli.CreatedAt = value.Time + } + case cartlineitems.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + cli.UpdatedAt = value.Time + } + case cartlineitems.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + cli.CreatedBy = value.String + } + case cartlineitems.FieldUpdatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field updated_by", values[i]) + } else if value.Valid { + cli.UpdatedBy = value.String + } + case cartlineitems.FieldMetadata: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field metadata", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &cli.Metadata); err != nil { + return fmt.Errorf("unmarshal field metadata: %w", err) + } + } + case cartlineitems.FieldCartID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field cart_id", values[i]) + } else if value.Valid { + cli.CartID = value.String + } + case cartlineitems.FieldEntityID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field entity_id", values[i]) + } else if value.Valid { + cli.EntityID = value.String + } + case cartlineitems.FieldEntityType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field entity_type", values[i]) + } else if value.Valid { + cli.EntityType = value.String + } + case cartlineitems.FieldQuantity: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field quantity", values[i]) + } else if value.Valid { + cli.Quantity = int(value.Int64) + } + case cartlineitems.FieldPerUnitPrice: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field per_unit_price", values[i]) + } else if value != nil { + cli.PerUnitPrice = *value + } + case cartlineitems.FieldTaxAmount: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field tax_amount", values[i]) + } else if value != nil { + cli.TaxAmount = *value + } + case cartlineitems.FieldDiscountAmount: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field discount_amount", values[i]) + } else if value != nil { + cli.DiscountAmount = *value + } + case cartlineitems.FieldSubtotal: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field subtotal", values[i]) + } else if value != nil { + cli.Subtotal = *value + } + case cartlineitems.FieldTotal: + if value, ok := values[i].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field total", values[i]) + } else if value != nil { + cli.Total = *value + } + default: + cli.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the CartLineItems. +// This includes values selected through modifiers, order, etc. +func (cli *CartLineItems) Value(name string) (ent.Value, error) { + return cli.selectValues.Get(name) +} + +// QueryCart queries the "cart" edge of the CartLineItems entity. +func (cli *CartLineItems) QueryCart() *CartQuery { + return NewCartLineItemsClient(cli.config).QueryCart(cli) +} + +// Update returns a builder for updating this CartLineItems. +// Note that you need to call CartLineItems.Unwrap() before calling this method if this CartLineItems +// was returned from a transaction, and the transaction was committed or rolled back. +func (cli *CartLineItems) Update() *CartLineItemsUpdateOne { + return NewCartLineItemsClient(cli.config).UpdateOne(cli) +} + +// Unwrap unwraps the CartLineItems entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (cli *CartLineItems) Unwrap() *CartLineItems { + _tx, ok := cli.config.driver.(*txDriver) + if !ok { + panic("ent: CartLineItems is not a transactional entity") + } + cli.config.driver = _tx.drv + return cli +} + +// String implements the fmt.Stringer. +func (cli *CartLineItems) String() string { + var builder strings.Builder + builder.WriteString("CartLineItems(") + builder.WriteString(fmt.Sprintf("id=%v, ", cli.ID)) + builder.WriteString("status=") + builder.WriteString(cli.Status) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(cli.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(cli.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(cli.CreatedBy) + builder.WriteString(", ") + builder.WriteString("updated_by=") + builder.WriteString(cli.UpdatedBy) + builder.WriteString(", ") + builder.WriteString("metadata=") + builder.WriteString(fmt.Sprintf("%v", cli.Metadata)) + builder.WriteString(", ") + builder.WriteString("cart_id=") + builder.WriteString(cli.CartID) + builder.WriteString(", ") + builder.WriteString("entity_id=") + builder.WriteString(cli.EntityID) + builder.WriteString(", ") + builder.WriteString("entity_type=") + builder.WriteString(cli.EntityType) + builder.WriteString(", ") + builder.WriteString("quantity=") + builder.WriteString(fmt.Sprintf("%v", cli.Quantity)) + builder.WriteString(", ") + builder.WriteString("per_unit_price=") + builder.WriteString(fmt.Sprintf("%v", cli.PerUnitPrice)) + builder.WriteString(", ") + builder.WriteString("tax_amount=") + builder.WriteString(fmt.Sprintf("%v", cli.TaxAmount)) + builder.WriteString(", ") + builder.WriteString("discount_amount=") + builder.WriteString(fmt.Sprintf("%v", cli.DiscountAmount)) + builder.WriteString(", ") + builder.WriteString("subtotal=") + builder.WriteString(fmt.Sprintf("%v", cli.Subtotal)) + builder.WriteString(", ") + builder.WriteString("total=") + builder.WriteString(fmt.Sprintf("%v", cli.Total)) + builder.WriteByte(')') + return builder.String() +} + +// CartLineItemsSlice is a parsable slice of CartLineItems. +type CartLineItemsSlice []*CartLineItems diff --git a/ent/cartlineitems/cartlineitems.go b/ent/cartlineitems/cartlineitems.go new file mode 100644 index 0000000..d95e81d --- /dev/null +++ b/ent/cartlineitems/cartlineitems.go @@ -0,0 +1,214 @@ +// Code generated by ent, DO NOT EDIT. + +package cartlineitems + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/shopspring/decimal" +) + +const ( + // Label holds the string label denoting the cartlineitems type in the database. + Label = "cart_line_items" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldUpdatedBy holds the string denoting the updated_by field in the database. + FieldUpdatedBy = "updated_by" + // FieldMetadata holds the string denoting the metadata field in the database. + FieldMetadata = "metadata" + // FieldCartID holds the string denoting the cart_id field in the database. + FieldCartID = "cart_id" + // FieldEntityID holds the string denoting the entity_id field in the database. + FieldEntityID = "entity_id" + // FieldEntityType holds the string denoting the entity_type field in the database. + FieldEntityType = "entity_type" + // FieldQuantity holds the string denoting the quantity field in the database. + FieldQuantity = "quantity" + // FieldPerUnitPrice holds the string denoting the per_unit_price field in the database. + FieldPerUnitPrice = "per_unit_price" + // FieldTaxAmount holds the string denoting the tax_amount field in the database. + FieldTaxAmount = "tax_amount" + // FieldDiscountAmount holds the string denoting the discount_amount field in the database. + FieldDiscountAmount = "discount_amount" + // FieldSubtotal holds the string denoting the subtotal field in the database. + FieldSubtotal = "subtotal" + // FieldTotal holds the string denoting the total field in the database. + FieldTotal = "total" + // EdgeCart holds the string denoting the cart edge name in mutations. + EdgeCart = "cart" + // Table holds the table name of the cartlineitems in the database. + Table = "cart_line_items" + // CartTable is the table that holds the cart relation/edge. + CartTable = "cart_line_items" + // CartInverseTable is the table name for the Cart entity. + // It exists in this package in order to avoid circular dependency with the "cart" package. + CartInverseTable = "carts" + // CartColumn is the table column denoting the cart relation/edge. + CartColumn = "cart_id" +) + +// Columns holds all SQL columns for cartlineitems fields. +var Columns = []string{ + FieldID, + FieldStatus, + FieldCreatedAt, + FieldUpdatedAt, + FieldCreatedBy, + FieldUpdatedBy, + FieldMetadata, + FieldCartID, + FieldEntityID, + FieldEntityType, + FieldQuantity, + FieldPerUnitPrice, + FieldTaxAmount, + FieldDiscountAmount, + FieldSubtotal, + FieldTotal, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultMetadata holds the default value on creation for the "metadata" field. + DefaultMetadata map[string]string + // CartIDValidator is a validator for the "cart_id" field. It is called by the builders before save. + CartIDValidator func(string) error + // EntityIDValidator is a validator for the "entity_id" field. It is called by the builders before save. + EntityIDValidator func(string) error + // EntityTypeValidator is a validator for the "entity_type" field. It is called by the builders before save. + EntityTypeValidator func(string) error + // DefaultQuantity holds the default value on creation for the "quantity" field. + DefaultQuantity int + // DefaultPerUnitPrice holds the default value on creation for the "per_unit_price" field. + DefaultPerUnitPrice decimal.Decimal + // DefaultTaxAmount holds the default value on creation for the "tax_amount" field. + DefaultTaxAmount decimal.Decimal + // DefaultDiscountAmount holds the default value on creation for the "discount_amount" field. + DefaultDiscountAmount decimal.Decimal + // DefaultSubtotal holds the default value on creation for the "subtotal" field. + DefaultSubtotal decimal.Decimal + // DefaultTotal holds the default value on creation for the "total" field. + DefaultTotal decimal.Decimal + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() string +) + +// OrderOption defines the ordering options for the CartLineItems queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByUpdatedBy orders the results by the updated_by field. +func ByUpdatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedBy, opts...).ToFunc() +} + +// ByCartID orders the results by the cart_id field. +func ByCartID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCartID, opts...).ToFunc() +} + +// ByEntityID orders the results by the entity_id field. +func ByEntityID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEntityID, opts...).ToFunc() +} + +// ByEntityType orders the results by the entity_type field. +func ByEntityType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEntityType, opts...).ToFunc() +} + +// ByQuantity orders the results by the quantity field. +func ByQuantity(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldQuantity, opts...).ToFunc() +} + +// ByPerUnitPrice orders the results by the per_unit_price field. +func ByPerUnitPrice(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPerUnitPrice, opts...).ToFunc() +} + +// ByTaxAmount orders the results by the tax_amount field. +func ByTaxAmount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTaxAmount, opts...).ToFunc() +} + +// ByDiscountAmount orders the results by the discount_amount field. +func ByDiscountAmount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDiscountAmount, opts...).ToFunc() +} + +// BySubtotal orders the results by the subtotal field. +func BySubtotal(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubtotal, opts...).ToFunc() +} + +// ByTotal orders the results by the total field. +func ByTotal(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTotal, opts...).ToFunc() +} + +// ByCartField orders the results by cart field. +func ByCartField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newCartStep(), sql.OrderByField(field, opts...)) + } +} +func newCartStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(CartInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, CartTable, CartColumn), + ) +} diff --git a/ent/cartlineitems/where.go b/ent/cartlineitems/where.go new file mode 100644 index 0000000..03c9d42 --- /dev/null +++ b/ent/cartlineitems/where.go @@ -0,0 +1,915 @@ +// Code generated by ent, DO NOT EDIT. + +package cartlineitems + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/omkar273/codegeeky/ent/predicate" + "github.com/shopspring/decimal" +) + +// ID filters vertices based on their ID field. +func ID(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldID, id)) +} + +// IDEqualFold applies the EqualFold predicate on the ID field. +func IDEqualFold(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEqualFold(FieldID, id)) +} + +// IDContainsFold applies the ContainsFold predicate on the ID field. +func IDContainsFold(id string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContainsFold(FieldID, id)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldStatus, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldCreatedBy, v)) +} + +// UpdatedBy applies equality check predicate on the "updated_by" field. It's identical to UpdatedByEQ. +func UpdatedBy(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// CartID applies equality check predicate on the "cart_id" field. It's identical to CartIDEQ. +func CartID(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldCartID, v)) +} + +// EntityID applies equality check predicate on the "entity_id" field. It's identical to EntityIDEQ. +func EntityID(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldEntityID, v)) +} + +// EntityType applies equality check predicate on the "entity_type" field. It's identical to EntityTypeEQ. +func EntityType(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldEntityType, v)) +} + +// Quantity applies equality check predicate on the "quantity" field. It's identical to QuantityEQ. +func Quantity(v int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldQuantity, v)) +} + +// PerUnitPrice applies equality check predicate on the "per_unit_price" field. It's identical to PerUnitPriceEQ. +func PerUnitPrice(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldPerUnitPrice, v)) +} + +// TaxAmount applies equality check predicate on the "tax_amount" field. It's identical to TaxAmountEQ. +func TaxAmount(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldTaxAmount, v)) +} + +// DiscountAmount applies equality check predicate on the "discount_amount" field. It's identical to DiscountAmountEQ. +func DiscountAmount(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldDiscountAmount, v)) +} + +// Subtotal applies equality check predicate on the "subtotal" field. It's identical to SubtotalEQ. +func Subtotal(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldSubtotal, v)) +} + +// Total applies equality check predicate on the "total" field. It's identical to TotalEQ. +func Total(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldTotal, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContainsFold(FieldStatus, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// UpdatedByEQ applies the EQ predicate on the "updated_by" field. +func UpdatedByEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UpdatedByNEQ applies the NEQ predicate on the "updated_by" field. +func UpdatedByNEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldUpdatedBy, v)) +} + +// UpdatedByIn applies the In predicate on the "updated_by" field. +func UpdatedByIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByNotIn applies the NotIn predicate on the "updated_by" field. +func UpdatedByNotIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByGT applies the GT predicate on the "updated_by" field. +func UpdatedByGT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldUpdatedBy, v)) +} + +// UpdatedByGTE applies the GTE predicate on the "updated_by" field. +func UpdatedByGTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldUpdatedBy, v)) +} + +// UpdatedByLT applies the LT predicate on the "updated_by" field. +func UpdatedByLT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldUpdatedBy, v)) +} + +// UpdatedByLTE applies the LTE predicate on the "updated_by" field. +func UpdatedByLTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldUpdatedBy, v)) +} + +// UpdatedByContains applies the Contains predicate on the "updated_by" field. +func UpdatedByContains(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContains(FieldUpdatedBy, v)) +} + +// UpdatedByHasPrefix applies the HasPrefix predicate on the "updated_by" field. +func UpdatedByHasPrefix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasPrefix(FieldUpdatedBy, v)) +} + +// UpdatedByHasSuffix applies the HasSuffix predicate on the "updated_by" field. +func UpdatedByHasSuffix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasSuffix(FieldUpdatedBy, v)) +} + +// UpdatedByIsNil applies the IsNil predicate on the "updated_by" field. +func UpdatedByIsNil() predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIsNull(FieldUpdatedBy)) +} + +// UpdatedByNotNil applies the NotNil predicate on the "updated_by" field. +func UpdatedByNotNil() predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotNull(FieldUpdatedBy)) +} + +// UpdatedByEqualFold applies the EqualFold predicate on the "updated_by" field. +func UpdatedByEqualFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEqualFold(FieldUpdatedBy, v)) +} + +// UpdatedByContainsFold applies the ContainsFold predicate on the "updated_by" field. +func UpdatedByContainsFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContainsFold(FieldUpdatedBy, v)) +} + +// MetadataIsNil applies the IsNil predicate on the "metadata" field. +func MetadataIsNil() predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIsNull(FieldMetadata)) +} + +// MetadataNotNil applies the NotNil predicate on the "metadata" field. +func MetadataNotNil() predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotNull(FieldMetadata)) +} + +// CartIDEQ applies the EQ predicate on the "cart_id" field. +func CartIDEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldCartID, v)) +} + +// CartIDNEQ applies the NEQ predicate on the "cart_id" field. +func CartIDNEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldCartID, v)) +} + +// CartIDIn applies the In predicate on the "cart_id" field. +func CartIDIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldCartID, vs...)) +} + +// CartIDNotIn applies the NotIn predicate on the "cart_id" field. +func CartIDNotIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldCartID, vs...)) +} + +// CartIDGT applies the GT predicate on the "cart_id" field. +func CartIDGT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldCartID, v)) +} + +// CartIDGTE applies the GTE predicate on the "cart_id" field. +func CartIDGTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldCartID, v)) +} + +// CartIDLT applies the LT predicate on the "cart_id" field. +func CartIDLT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldCartID, v)) +} + +// CartIDLTE applies the LTE predicate on the "cart_id" field. +func CartIDLTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldCartID, v)) +} + +// CartIDContains applies the Contains predicate on the "cart_id" field. +func CartIDContains(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContains(FieldCartID, v)) +} + +// CartIDHasPrefix applies the HasPrefix predicate on the "cart_id" field. +func CartIDHasPrefix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasPrefix(FieldCartID, v)) +} + +// CartIDHasSuffix applies the HasSuffix predicate on the "cart_id" field. +func CartIDHasSuffix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasSuffix(FieldCartID, v)) +} + +// CartIDEqualFold applies the EqualFold predicate on the "cart_id" field. +func CartIDEqualFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEqualFold(FieldCartID, v)) +} + +// CartIDContainsFold applies the ContainsFold predicate on the "cart_id" field. +func CartIDContainsFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContainsFold(FieldCartID, v)) +} + +// EntityIDEQ applies the EQ predicate on the "entity_id" field. +func EntityIDEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldEntityID, v)) +} + +// EntityIDNEQ applies the NEQ predicate on the "entity_id" field. +func EntityIDNEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldEntityID, v)) +} + +// EntityIDIn applies the In predicate on the "entity_id" field. +func EntityIDIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldEntityID, vs...)) +} + +// EntityIDNotIn applies the NotIn predicate on the "entity_id" field. +func EntityIDNotIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldEntityID, vs...)) +} + +// EntityIDGT applies the GT predicate on the "entity_id" field. +func EntityIDGT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldEntityID, v)) +} + +// EntityIDGTE applies the GTE predicate on the "entity_id" field. +func EntityIDGTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldEntityID, v)) +} + +// EntityIDLT applies the LT predicate on the "entity_id" field. +func EntityIDLT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldEntityID, v)) +} + +// EntityIDLTE applies the LTE predicate on the "entity_id" field. +func EntityIDLTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldEntityID, v)) +} + +// EntityIDContains applies the Contains predicate on the "entity_id" field. +func EntityIDContains(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContains(FieldEntityID, v)) +} + +// EntityIDHasPrefix applies the HasPrefix predicate on the "entity_id" field. +func EntityIDHasPrefix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasPrefix(FieldEntityID, v)) +} + +// EntityIDHasSuffix applies the HasSuffix predicate on the "entity_id" field. +func EntityIDHasSuffix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasSuffix(FieldEntityID, v)) +} + +// EntityIDEqualFold applies the EqualFold predicate on the "entity_id" field. +func EntityIDEqualFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEqualFold(FieldEntityID, v)) +} + +// EntityIDContainsFold applies the ContainsFold predicate on the "entity_id" field. +func EntityIDContainsFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContainsFold(FieldEntityID, v)) +} + +// EntityTypeEQ applies the EQ predicate on the "entity_type" field. +func EntityTypeEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldEntityType, v)) +} + +// EntityTypeNEQ applies the NEQ predicate on the "entity_type" field. +func EntityTypeNEQ(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldEntityType, v)) +} + +// EntityTypeIn applies the In predicate on the "entity_type" field. +func EntityTypeIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldEntityType, vs...)) +} + +// EntityTypeNotIn applies the NotIn predicate on the "entity_type" field. +func EntityTypeNotIn(vs ...string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldEntityType, vs...)) +} + +// EntityTypeGT applies the GT predicate on the "entity_type" field. +func EntityTypeGT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldEntityType, v)) +} + +// EntityTypeGTE applies the GTE predicate on the "entity_type" field. +func EntityTypeGTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldEntityType, v)) +} + +// EntityTypeLT applies the LT predicate on the "entity_type" field. +func EntityTypeLT(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldEntityType, v)) +} + +// EntityTypeLTE applies the LTE predicate on the "entity_type" field. +func EntityTypeLTE(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldEntityType, v)) +} + +// EntityTypeContains applies the Contains predicate on the "entity_type" field. +func EntityTypeContains(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContains(FieldEntityType, v)) +} + +// EntityTypeHasPrefix applies the HasPrefix predicate on the "entity_type" field. +func EntityTypeHasPrefix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasPrefix(FieldEntityType, v)) +} + +// EntityTypeHasSuffix applies the HasSuffix predicate on the "entity_type" field. +func EntityTypeHasSuffix(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldHasSuffix(FieldEntityType, v)) +} + +// EntityTypeEqualFold applies the EqualFold predicate on the "entity_type" field. +func EntityTypeEqualFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEqualFold(FieldEntityType, v)) +} + +// EntityTypeContainsFold applies the ContainsFold predicate on the "entity_type" field. +func EntityTypeContainsFold(v string) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldContainsFold(FieldEntityType, v)) +} + +// QuantityEQ applies the EQ predicate on the "quantity" field. +func QuantityEQ(v int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldQuantity, v)) +} + +// QuantityNEQ applies the NEQ predicate on the "quantity" field. +func QuantityNEQ(v int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldQuantity, v)) +} + +// QuantityIn applies the In predicate on the "quantity" field. +func QuantityIn(vs ...int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldQuantity, vs...)) +} + +// QuantityNotIn applies the NotIn predicate on the "quantity" field. +func QuantityNotIn(vs ...int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldQuantity, vs...)) +} + +// QuantityGT applies the GT predicate on the "quantity" field. +func QuantityGT(v int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldQuantity, v)) +} + +// QuantityGTE applies the GTE predicate on the "quantity" field. +func QuantityGTE(v int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldQuantity, v)) +} + +// QuantityLT applies the LT predicate on the "quantity" field. +func QuantityLT(v int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldQuantity, v)) +} + +// QuantityLTE applies the LTE predicate on the "quantity" field. +func QuantityLTE(v int) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldQuantity, v)) +} + +// PerUnitPriceEQ applies the EQ predicate on the "per_unit_price" field. +func PerUnitPriceEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldPerUnitPrice, v)) +} + +// PerUnitPriceNEQ applies the NEQ predicate on the "per_unit_price" field. +func PerUnitPriceNEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldPerUnitPrice, v)) +} + +// PerUnitPriceIn applies the In predicate on the "per_unit_price" field. +func PerUnitPriceIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldPerUnitPrice, vs...)) +} + +// PerUnitPriceNotIn applies the NotIn predicate on the "per_unit_price" field. +func PerUnitPriceNotIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldPerUnitPrice, vs...)) +} + +// PerUnitPriceGT applies the GT predicate on the "per_unit_price" field. +func PerUnitPriceGT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldPerUnitPrice, v)) +} + +// PerUnitPriceGTE applies the GTE predicate on the "per_unit_price" field. +func PerUnitPriceGTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldPerUnitPrice, v)) +} + +// PerUnitPriceLT applies the LT predicate on the "per_unit_price" field. +func PerUnitPriceLT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldPerUnitPrice, v)) +} + +// PerUnitPriceLTE applies the LTE predicate on the "per_unit_price" field. +func PerUnitPriceLTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldPerUnitPrice, v)) +} + +// TaxAmountEQ applies the EQ predicate on the "tax_amount" field. +func TaxAmountEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldTaxAmount, v)) +} + +// TaxAmountNEQ applies the NEQ predicate on the "tax_amount" field. +func TaxAmountNEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldTaxAmount, v)) +} + +// TaxAmountIn applies the In predicate on the "tax_amount" field. +func TaxAmountIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldTaxAmount, vs...)) +} + +// TaxAmountNotIn applies the NotIn predicate on the "tax_amount" field. +func TaxAmountNotIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldTaxAmount, vs...)) +} + +// TaxAmountGT applies the GT predicate on the "tax_amount" field. +func TaxAmountGT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldTaxAmount, v)) +} + +// TaxAmountGTE applies the GTE predicate on the "tax_amount" field. +func TaxAmountGTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldTaxAmount, v)) +} + +// TaxAmountLT applies the LT predicate on the "tax_amount" field. +func TaxAmountLT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldTaxAmount, v)) +} + +// TaxAmountLTE applies the LTE predicate on the "tax_amount" field. +func TaxAmountLTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldTaxAmount, v)) +} + +// DiscountAmountEQ applies the EQ predicate on the "discount_amount" field. +func DiscountAmountEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldDiscountAmount, v)) +} + +// DiscountAmountNEQ applies the NEQ predicate on the "discount_amount" field. +func DiscountAmountNEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldDiscountAmount, v)) +} + +// DiscountAmountIn applies the In predicate on the "discount_amount" field. +func DiscountAmountIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldDiscountAmount, vs...)) +} + +// DiscountAmountNotIn applies the NotIn predicate on the "discount_amount" field. +func DiscountAmountNotIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldDiscountAmount, vs...)) +} + +// DiscountAmountGT applies the GT predicate on the "discount_amount" field. +func DiscountAmountGT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldDiscountAmount, v)) +} + +// DiscountAmountGTE applies the GTE predicate on the "discount_amount" field. +func DiscountAmountGTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldDiscountAmount, v)) +} + +// DiscountAmountLT applies the LT predicate on the "discount_amount" field. +func DiscountAmountLT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldDiscountAmount, v)) +} + +// DiscountAmountLTE applies the LTE predicate on the "discount_amount" field. +func DiscountAmountLTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldDiscountAmount, v)) +} + +// SubtotalEQ applies the EQ predicate on the "subtotal" field. +func SubtotalEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldSubtotal, v)) +} + +// SubtotalNEQ applies the NEQ predicate on the "subtotal" field. +func SubtotalNEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldSubtotal, v)) +} + +// SubtotalIn applies the In predicate on the "subtotal" field. +func SubtotalIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldSubtotal, vs...)) +} + +// SubtotalNotIn applies the NotIn predicate on the "subtotal" field. +func SubtotalNotIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldSubtotal, vs...)) +} + +// SubtotalGT applies the GT predicate on the "subtotal" field. +func SubtotalGT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldSubtotal, v)) +} + +// SubtotalGTE applies the GTE predicate on the "subtotal" field. +func SubtotalGTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldSubtotal, v)) +} + +// SubtotalLT applies the LT predicate on the "subtotal" field. +func SubtotalLT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldSubtotal, v)) +} + +// SubtotalLTE applies the LTE predicate on the "subtotal" field. +func SubtotalLTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldSubtotal, v)) +} + +// TotalEQ applies the EQ predicate on the "total" field. +func TotalEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldEQ(FieldTotal, v)) +} + +// TotalNEQ applies the NEQ predicate on the "total" field. +func TotalNEQ(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNEQ(FieldTotal, v)) +} + +// TotalIn applies the In predicate on the "total" field. +func TotalIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldIn(FieldTotal, vs...)) +} + +// TotalNotIn applies the NotIn predicate on the "total" field. +func TotalNotIn(vs ...decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldNotIn(FieldTotal, vs...)) +} + +// TotalGT applies the GT predicate on the "total" field. +func TotalGT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGT(FieldTotal, v)) +} + +// TotalGTE applies the GTE predicate on the "total" field. +func TotalGTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldGTE(FieldTotal, v)) +} + +// TotalLT applies the LT predicate on the "total" field. +func TotalLT(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLT(FieldTotal, v)) +} + +// TotalLTE applies the LTE predicate on the "total" field. +func TotalLTE(v decimal.Decimal) predicate.CartLineItems { + return predicate.CartLineItems(sql.FieldLTE(FieldTotal, v)) +} + +// HasCart applies the HasEdge predicate on the "cart" edge. +func HasCart() predicate.CartLineItems { + return predicate.CartLineItems(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, CartTable, CartColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasCartWith applies the HasEdge predicate on the "cart" edge with a given conditions (other predicates). +func HasCartWith(preds ...predicate.Cart) predicate.CartLineItems { + return predicate.CartLineItems(func(s *sql.Selector) { + step := newCartStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.CartLineItems) predicate.CartLineItems { + return predicate.CartLineItems(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.CartLineItems) predicate.CartLineItems { + return predicate.CartLineItems(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.CartLineItems) predicate.CartLineItems { + return predicate.CartLineItems(sql.NotPredicates(p)) +} diff --git a/ent/cartlineitems_create.go b/ent/cartlineitems_create.go new file mode 100644 index 0000000..186ec07 --- /dev/null +++ b/ent/cartlineitems_create.go @@ -0,0 +1,552 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" + "github.com/shopspring/decimal" +) + +// CartLineItemsCreate is the builder for creating a CartLineItems entity. +type CartLineItemsCreate struct { + config + mutation *CartLineItemsMutation + hooks []Hook +} + +// SetStatus sets the "status" field. +func (clic *CartLineItemsCreate) SetStatus(s string) *CartLineItemsCreate { + clic.mutation.SetStatus(s) + return clic +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableStatus(s *string) *CartLineItemsCreate { + if s != nil { + clic.SetStatus(*s) + } + return clic +} + +// SetCreatedAt sets the "created_at" field. +func (clic *CartLineItemsCreate) SetCreatedAt(t time.Time) *CartLineItemsCreate { + clic.mutation.SetCreatedAt(t) + return clic +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableCreatedAt(t *time.Time) *CartLineItemsCreate { + if t != nil { + clic.SetCreatedAt(*t) + } + return clic +} + +// SetUpdatedAt sets the "updated_at" field. +func (clic *CartLineItemsCreate) SetUpdatedAt(t time.Time) *CartLineItemsCreate { + clic.mutation.SetUpdatedAt(t) + return clic +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableUpdatedAt(t *time.Time) *CartLineItemsCreate { + if t != nil { + clic.SetUpdatedAt(*t) + } + return clic +} + +// SetCreatedBy sets the "created_by" field. +func (clic *CartLineItemsCreate) SetCreatedBy(s string) *CartLineItemsCreate { + clic.mutation.SetCreatedBy(s) + return clic +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableCreatedBy(s *string) *CartLineItemsCreate { + if s != nil { + clic.SetCreatedBy(*s) + } + return clic +} + +// SetUpdatedBy sets the "updated_by" field. +func (clic *CartLineItemsCreate) SetUpdatedBy(s string) *CartLineItemsCreate { + clic.mutation.SetUpdatedBy(s) + return clic +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableUpdatedBy(s *string) *CartLineItemsCreate { + if s != nil { + clic.SetUpdatedBy(*s) + } + return clic +} + +// SetMetadata sets the "metadata" field. +func (clic *CartLineItemsCreate) SetMetadata(m map[string]string) *CartLineItemsCreate { + clic.mutation.SetMetadata(m) + return clic +} + +// SetCartID sets the "cart_id" field. +func (clic *CartLineItemsCreate) SetCartID(s string) *CartLineItemsCreate { + clic.mutation.SetCartID(s) + return clic +} + +// SetEntityID sets the "entity_id" field. +func (clic *CartLineItemsCreate) SetEntityID(s string) *CartLineItemsCreate { + clic.mutation.SetEntityID(s) + return clic +} + +// SetEntityType sets the "entity_type" field. +func (clic *CartLineItemsCreate) SetEntityType(s string) *CartLineItemsCreate { + clic.mutation.SetEntityType(s) + return clic +} + +// SetQuantity sets the "quantity" field. +func (clic *CartLineItemsCreate) SetQuantity(i int) *CartLineItemsCreate { + clic.mutation.SetQuantity(i) + return clic +} + +// SetNillableQuantity sets the "quantity" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableQuantity(i *int) *CartLineItemsCreate { + if i != nil { + clic.SetQuantity(*i) + } + return clic +} + +// SetPerUnitPrice sets the "per_unit_price" field. +func (clic *CartLineItemsCreate) SetPerUnitPrice(d decimal.Decimal) *CartLineItemsCreate { + clic.mutation.SetPerUnitPrice(d) + return clic +} + +// SetNillablePerUnitPrice sets the "per_unit_price" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillablePerUnitPrice(d *decimal.Decimal) *CartLineItemsCreate { + if d != nil { + clic.SetPerUnitPrice(*d) + } + return clic +} + +// SetTaxAmount sets the "tax_amount" field. +func (clic *CartLineItemsCreate) SetTaxAmount(d decimal.Decimal) *CartLineItemsCreate { + clic.mutation.SetTaxAmount(d) + return clic +} + +// SetNillableTaxAmount sets the "tax_amount" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableTaxAmount(d *decimal.Decimal) *CartLineItemsCreate { + if d != nil { + clic.SetTaxAmount(*d) + } + return clic +} + +// SetDiscountAmount sets the "discount_amount" field. +func (clic *CartLineItemsCreate) SetDiscountAmount(d decimal.Decimal) *CartLineItemsCreate { + clic.mutation.SetDiscountAmount(d) + return clic +} + +// SetNillableDiscountAmount sets the "discount_amount" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableDiscountAmount(d *decimal.Decimal) *CartLineItemsCreate { + if d != nil { + clic.SetDiscountAmount(*d) + } + return clic +} + +// SetSubtotal sets the "subtotal" field. +func (clic *CartLineItemsCreate) SetSubtotal(d decimal.Decimal) *CartLineItemsCreate { + clic.mutation.SetSubtotal(d) + return clic +} + +// SetNillableSubtotal sets the "subtotal" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableSubtotal(d *decimal.Decimal) *CartLineItemsCreate { + if d != nil { + clic.SetSubtotal(*d) + } + return clic +} + +// SetTotal sets the "total" field. +func (clic *CartLineItemsCreate) SetTotal(d decimal.Decimal) *CartLineItemsCreate { + clic.mutation.SetTotal(d) + return clic +} + +// SetNillableTotal sets the "total" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableTotal(d *decimal.Decimal) *CartLineItemsCreate { + if d != nil { + clic.SetTotal(*d) + } + return clic +} + +// SetID sets the "id" field. +func (clic *CartLineItemsCreate) SetID(s string) *CartLineItemsCreate { + clic.mutation.SetID(s) + return clic +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (clic *CartLineItemsCreate) SetNillableID(s *string) *CartLineItemsCreate { + if s != nil { + clic.SetID(*s) + } + return clic +} + +// SetCart sets the "cart" edge to the Cart entity. +func (clic *CartLineItemsCreate) SetCart(c *Cart) *CartLineItemsCreate { + return clic.SetCartID(c.ID) +} + +// Mutation returns the CartLineItemsMutation object of the builder. +func (clic *CartLineItemsCreate) Mutation() *CartLineItemsMutation { + return clic.mutation +} + +// Save creates the CartLineItems in the database. +func (clic *CartLineItemsCreate) Save(ctx context.Context) (*CartLineItems, error) { + clic.defaults() + return withHooks(ctx, clic.sqlSave, clic.mutation, clic.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (clic *CartLineItemsCreate) SaveX(ctx context.Context) *CartLineItems { + v, err := clic.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (clic *CartLineItemsCreate) Exec(ctx context.Context) error { + _, err := clic.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (clic *CartLineItemsCreate) ExecX(ctx context.Context) { + if err := clic.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (clic *CartLineItemsCreate) defaults() { + if _, ok := clic.mutation.Status(); !ok { + v := cartlineitems.DefaultStatus + clic.mutation.SetStatus(v) + } + if _, ok := clic.mutation.CreatedAt(); !ok { + v := cartlineitems.DefaultCreatedAt() + clic.mutation.SetCreatedAt(v) + } + if _, ok := clic.mutation.UpdatedAt(); !ok { + v := cartlineitems.DefaultUpdatedAt() + clic.mutation.SetUpdatedAt(v) + } + if _, ok := clic.mutation.Metadata(); !ok { + v := cartlineitems.DefaultMetadata + clic.mutation.SetMetadata(v) + } + if _, ok := clic.mutation.Quantity(); !ok { + v := cartlineitems.DefaultQuantity + clic.mutation.SetQuantity(v) + } + if _, ok := clic.mutation.PerUnitPrice(); !ok { + v := cartlineitems.DefaultPerUnitPrice + clic.mutation.SetPerUnitPrice(v) + } + if _, ok := clic.mutation.TaxAmount(); !ok { + v := cartlineitems.DefaultTaxAmount + clic.mutation.SetTaxAmount(v) + } + if _, ok := clic.mutation.DiscountAmount(); !ok { + v := cartlineitems.DefaultDiscountAmount + clic.mutation.SetDiscountAmount(v) + } + if _, ok := clic.mutation.Subtotal(); !ok { + v := cartlineitems.DefaultSubtotal + clic.mutation.SetSubtotal(v) + } + if _, ok := clic.mutation.Total(); !ok { + v := cartlineitems.DefaultTotal + clic.mutation.SetTotal(v) + } + if _, ok := clic.mutation.ID(); !ok { + v := cartlineitems.DefaultID() + clic.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (clic *CartLineItemsCreate) check() error { + if _, ok := clic.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "CartLineItems.status"`)} + } + if _, ok := clic.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "CartLineItems.created_at"`)} + } + if _, ok := clic.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "CartLineItems.updated_at"`)} + } + if _, ok := clic.mutation.CartID(); !ok { + return &ValidationError{Name: "cart_id", err: errors.New(`ent: missing required field "CartLineItems.cart_id"`)} + } + if v, ok := clic.mutation.CartID(); ok { + if err := cartlineitems.CartIDValidator(v); err != nil { + return &ValidationError{Name: "cart_id", err: fmt.Errorf(`ent: validator failed for field "CartLineItems.cart_id": %w`, err)} + } + } + if _, ok := clic.mutation.EntityID(); !ok { + return &ValidationError{Name: "entity_id", err: errors.New(`ent: missing required field "CartLineItems.entity_id"`)} + } + if v, ok := clic.mutation.EntityID(); ok { + if err := cartlineitems.EntityIDValidator(v); err != nil { + return &ValidationError{Name: "entity_id", err: fmt.Errorf(`ent: validator failed for field "CartLineItems.entity_id": %w`, err)} + } + } + if _, ok := clic.mutation.EntityType(); !ok { + return &ValidationError{Name: "entity_type", err: errors.New(`ent: missing required field "CartLineItems.entity_type"`)} + } + if v, ok := clic.mutation.EntityType(); ok { + if err := cartlineitems.EntityTypeValidator(v); err != nil { + return &ValidationError{Name: "entity_type", err: fmt.Errorf(`ent: validator failed for field "CartLineItems.entity_type": %w`, err)} + } + } + if _, ok := clic.mutation.Quantity(); !ok { + return &ValidationError{Name: "quantity", err: errors.New(`ent: missing required field "CartLineItems.quantity"`)} + } + if _, ok := clic.mutation.PerUnitPrice(); !ok { + return &ValidationError{Name: "per_unit_price", err: errors.New(`ent: missing required field "CartLineItems.per_unit_price"`)} + } + if _, ok := clic.mutation.TaxAmount(); !ok { + return &ValidationError{Name: "tax_amount", err: errors.New(`ent: missing required field "CartLineItems.tax_amount"`)} + } + if _, ok := clic.mutation.DiscountAmount(); !ok { + return &ValidationError{Name: "discount_amount", err: errors.New(`ent: missing required field "CartLineItems.discount_amount"`)} + } + if _, ok := clic.mutation.Subtotal(); !ok { + return &ValidationError{Name: "subtotal", err: errors.New(`ent: missing required field "CartLineItems.subtotal"`)} + } + if _, ok := clic.mutation.Total(); !ok { + return &ValidationError{Name: "total", err: errors.New(`ent: missing required field "CartLineItems.total"`)} + } + if len(clic.mutation.CartIDs()) == 0 { + return &ValidationError{Name: "cart", err: errors.New(`ent: missing required edge "CartLineItems.cart"`)} + } + return nil +} + +func (clic *CartLineItemsCreate) sqlSave(ctx context.Context) (*CartLineItems, error) { + if err := clic.check(); err != nil { + return nil, err + } + _node, _spec := clic.createSpec() + if err := sqlgraph.CreateNode(ctx, clic.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(string); ok { + _node.ID = id + } else { + return nil, fmt.Errorf("unexpected CartLineItems.ID type: %T", _spec.ID.Value) + } + } + clic.mutation.id = &_node.ID + clic.mutation.done = true + return _node, nil +} + +func (clic *CartLineItemsCreate) createSpec() (*CartLineItems, *sqlgraph.CreateSpec) { + var ( + _node = &CartLineItems{config: clic.config} + _spec = sqlgraph.NewCreateSpec(cartlineitems.Table, sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString)) + ) + if id, ok := clic.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := clic.mutation.Status(); ok { + _spec.SetField(cartlineitems.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := clic.mutation.CreatedAt(); ok { + _spec.SetField(cartlineitems.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := clic.mutation.UpdatedAt(); ok { + _spec.SetField(cartlineitems.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := clic.mutation.CreatedBy(); ok { + _spec.SetField(cartlineitems.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := clic.mutation.UpdatedBy(); ok { + _spec.SetField(cartlineitems.FieldUpdatedBy, field.TypeString, value) + _node.UpdatedBy = value + } + if value, ok := clic.mutation.Metadata(); ok { + _spec.SetField(cartlineitems.FieldMetadata, field.TypeJSON, value) + _node.Metadata = value + } + if value, ok := clic.mutation.EntityID(); ok { + _spec.SetField(cartlineitems.FieldEntityID, field.TypeString, value) + _node.EntityID = value + } + if value, ok := clic.mutation.EntityType(); ok { + _spec.SetField(cartlineitems.FieldEntityType, field.TypeString, value) + _node.EntityType = value + } + if value, ok := clic.mutation.Quantity(); ok { + _spec.SetField(cartlineitems.FieldQuantity, field.TypeInt, value) + _node.Quantity = value + } + if value, ok := clic.mutation.PerUnitPrice(); ok { + _spec.SetField(cartlineitems.FieldPerUnitPrice, field.TypeOther, value) + _node.PerUnitPrice = value + } + if value, ok := clic.mutation.TaxAmount(); ok { + _spec.SetField(cartlineitems.FieldTaxAmount, field.TypeOther, value) + _node.TaxAmount = value + } + if value, ok := clic.mutation.DiscountAmount(); ok { + _spec.SetField(cartlineitems.FieldDiscountAmount, field.TypeOther, value) + _node.DiscountAmount = value + } + if value, ok := clic.mutation.Subtotal(); ok { + _spec.SetField(cartlineitems.FieldSubtotal, field.TypeOther, value) + _node.Subtotal = value + } + if value, ok := clic.mutation.Total(); ok { + _spec.SetField(cartlineitems.FieldTotal, field.TypeOther, value) + _node.Total = value + } + if nodes := clic.mutation.CartIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: cartlineitems.CartTable, + Columns: []string{cartlineitems.CartColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.CartID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// CartLineItemsCreateBulk is the builder for creating many CartLineItems entities in bulk. +type CartLineItemsCreateBulk struct { + config + err error + builders []*CartLineItemsCreate +} + +// Save creates the CartLineItems entities in the database. +func (clicb *CartLineItemsCreateBulk) Save(ctx context.Context) ([]*CartLineItems, error) { + if clicb.err != nil { + return nil, clicb.err + } + specs := make([]*sqlgraph.CreateSpec, len(clicb.builders)) + nodes := make([]*CartLineItems, len(clicb.builders)) + mutators := make([]Mutator, len(clicb.builders)) + for i := range clicb.builders { + func(i int, root context.Context) { + builder := clicb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*CartLineItemsMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, clicb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, clicb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, clicb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (clicb *CartLineItemsCreateBulk) SaveX(ctx context.Context) []*CartLineItems { + v, err := clicb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (clicb *CartLineItemsCreateBulk) Exec(ctx context.Context) error { + _, err := clicb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (clicb *CartLineItemsCreateBulk) ExecX(ctx context.Context) { + if err := clicb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/cartlineitems_delete.go b/ent/cartlineitems_delete.go new file mode 100644 index 0000000..138e4b3 --- /dev/null +++ b/ent/cartlineitems_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cartlineitems" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// CartLineItemsDelete is the builder for deleting a CartLineItems entity. +type CartLineItemsDelete struct { + config + hooks []Hook + mutation *CartLineItemsMutation +} + +// Where appends a list predicates to the CartLineItemsDelete builder. +func (clid *CartLineItemsDelete) Where(ps ...predicate.CartLineItems) *CartLineItemsDelete { + clid.mutation.Where(ps...) + return clid +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (clid *CartLineItemsDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, clid.sqlExec, clid.mutation, clid.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (clid *CartLineItemsDelete) ExecX(ctx context.Context) int { + n, err := clid.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (clid *CartLineItemsDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(cartlineitems.Table, sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString)) + if ps := clid.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, clid.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + clid.mutation.done = true + return affected, err +} + +// CartLineItemsDeleteOne is the builder for deleting a single CartLineItems entity. +type CartLineItemsDeleteOne struct { + clid *CartLineItemsDelete +} + +// Where appends a list predicates to the CartLineItemsDelete builder. +func (clido *CartLineItemsDeleteOne) Where(ps ...predicate.CartLineItems) *CartLineItemsDeleteOne { + clido.clid.mutation.Where(ps...) + return clido +} + +// Exec executes the deletion query. +func (clido *CartLineItemsDeleteOne) Exec(ctx context.Context) error { + n, err := clido.clid.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{cartlineitems.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (clido *CartLineItemsDeleteOne) ExecX(ctx context.Context) { + if err := clido.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/cartlineitems_query.go b/ent/cartlineitems_query.go new file mode 100644 index 0000000..c64630a --- /dev/null +++ b/ent/cartlineitems_query.go @@ -0,0 +1,606 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// CartLineItemsQuery is the builder for querying CartLineItems entities. +type CartLineItemsQuery struct { + config + ctx *QueryContext + order []cartlineitems.OrderOption + inters []Interceptor + predicates []predicate.CartLineItems + withCart *CartQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the CartLineItemsQuery builder. +func (cliq *CartLineItemsQuery) Where(ps ...predicate.CartLineItems) *CartLineItemsQuery { + cliq.predicates = append(cliq.predicates, ps...) + return cliq +} + +// Limit the number of records to be returned by this query. +func (cliq *CartLineItemsQuery) Limit(limit int) *CartLineItemsQuery { + cliq.ctx.Limit = &limit + return cliq +} + +// Offset to start from. +func (cliq *CartLineItemsQuery) Offset(offset int) *CartLineItemsQuery { + cliq.ctx.Offset = &offset + return cliq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (cliq *CartLineItemsQuery) Unique(unique bool) *CartLineItemsQuery { + cliq.ctx.Unique = &unique + return cliq +} + +// Order specifies how the records should be ordered. +func (cliq *CartLineItemsQuery) Order(o ...cartlineitems.OrderOption) *CartLineItemsQuery { + cliq.order = append(cliq.order, o...) + return cliq +} + +// QueryCart chains the current query on the "cart" edge. +func (cliq *CartLineItemsQuery) QueryCart() *CartQuery { + query := (&CartClient{config: cliq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := cliq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := cliq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(cartlineitems.Table, cartlineitems.FieldID, selector), + sqlgraph.To(cart.Table, cart.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, cartlineitems.CartTable, cartlineitems.CartColumn), + ) + fromU = sqlgraph.SetNeighbors(cliq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first CartLineItems entity from the query. +// Returns a *NotFoundError when no CartLineItems was found. +func (cliq *CartLineItemsQuery) First(ctx context.Context) (*CartLineItems, error) { + nodes, err := cliq.Limit(1).All(setContextOp(ctx, cliq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{cartlineitems.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (cliq *CartLineItemsQuery) FirstX(ctx context.Context) *CartLineItems { + node, err := cliq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first CartLineItems ID from the query. +// Returns a *NotFoundError when no CartLineItems ID was found. +func (cliq *CartLineItemsQuery) FirstID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = cliq.Limit(1).IDs(setContextOp(ctx, cliq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{cartlineitems.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (cliq *CartLineItemsQuery) FirstIDX(ctx context.Context) string { + id, err := cliq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single CartLineItems entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one CartLineItems entity is found. +// Returns a *NotFoundError when no CartLineItems entities are found. +func (cliq *CartLineItemsQuery) Only(ctx context.Context) (*CartLineItems, error) { + nodes, err := cliq.Limit(2).All(setContextOp(ctx, cliq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{cartlineitems.Label} + default: + return nil, &NotSingularError{cartlineitems.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (cliq *CartLineItemsQuery) OnlyX(ctx context.Context) *CartLineItems { + node, err := cliq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only CartLineItems ID in the query. +// Returns a *NotSingularError when more than one CartLineItems ID is found. +// Returns a *NotFoundError when no entities are found. +func (cliq *CartLineItemsQuery) OnlyID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = cliq.Limit(2).IDs(setContextOp(ctx, cliq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{cartlineitems.Label} + default: + err = &NotSingularError{cartlineitems.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (cliq *CartLineItemsQuery) OnlyIDX(ctx context.Context) string { + id, err := cliq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of CartLineItemsSlice. +func (cliq *CartLineItemsQuery) All(ctx context.Context) ([]*CartLineItems, error) { + ctx = setContextOp(ctx, cliq.ctx, ent.OpQueryAll) + if err := cliq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*CartLineItems, *CartLineItemsQuery]() + return withInterceptors[[]*CartLineItems](ctx, cliq, qr, cliq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (cliq *CartLineItemsQuery) AllX(ctx context.Context) []*CartLineItems { + nodes, err := cliq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of CartLineItems IDs. +func (cliq *CartLineItemsQuery) IDs(ctx context.Context) (ids []string, err error) { + if cliq.ctx.Unique == nil && cliq.path != nil { + cliq.Unique(true) + } + ctx = setContextOp(ctx, cliq.ctx, ent.OpQueryIDs) + if err = cliq.Select(cartlineitems.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (cliq *CartLineItemsQuery) IDsX(ctx context.Context) []string { + ids, err := cliq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (cliq *CartLineItemsQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, cliq.ctx, ent.OpQueryCount) + if err := cliq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, cliq, querierCount[*CartLineItemsQuery](), cliq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (cliq *CartLineItemsQuery) CountX(ctx context.Context) int { + count, err := cliq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (cliq *CartLineItemsQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, cliq.ctx, ent.OpQueryExist) + switch _, err := cliq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (cliq *CartLineItemsQuery) ExistX(ctx context.Context) bool { + exist, err := cliq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the CartLineItemsQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (cliq *CartLineItemsQuery) Clone() *CartLineItemsQuery { + if cliq == nil { + return nil + } + return &CartLineItemsQuery{ + config: cliq.config, + ctx: cliq.ctx.Clone(), + order: append([]cartlineitems.OrderOption{}, cliq.order...), + inters: append([]Interceptor{}, cliq.inters...), + predicates: append([]predicate.CartLineItems{}, cliq.predicates...), + withCart: cliq.withCart.Clone(), + // clone intermediate query. + sql: cliq.sql.Clone(), + path: cliq.path, + } +} + +// WithCart tells the query-builder to eager-load the nodes that are connected to +// the "cart" edge. The optional arguments are used to configure the query builder of the edge. +func (cliq *CartLineItemsQuery) WithCart(opts ...func(*CartQuery)) *CartLineItemsQuery { + query := (&CartClient{config: cliq.config}).Query() + for _, opt := range opts { + opt(query) + } + cliq.withCart = query + return cliq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Status string `json:"status,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.CartLineItems.Query(). +// GroupBy(cartlineitems.FieldStatus). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (cliq *CartLineItemsQuery) GroupBy(field string, fields ...string) *CartLineItemsGroupBy { + cliq.ctx.Fields = append([]string{field}, fields...) + grbuild := &CartLineItemsGroupBy{build: cliq} + grbuild.flds = &cliq.ctx.Fields + grbuild.label = cartlineitems.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Status string `json:"status,omitempty"` +// } +// +// client.CartLineItems.Query(). +// Select(cartlineitems.FieldStatus). +// Scan(ctx, &v) +func (cliq *CartLineItemsQuery) Select(fields ...string) *CartLineItemsSelect { + cliq.ctx.Fields = append(cliq.ctx.Fields, fields...) + sbuild := &CartLineItemsSelect{CartLineItemsQuery: cliq} + sbuild.label = cartlineitems.Label + sbuild.flds, sbuild.scan = &cliq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a CartLineItemsSelect configured with the given aggregations. +func (cliq *CartLineItemsQuery) Aggregate(fns ...AggregateFunc) *CartLineItemsSelect { + return cliq.Select().Aggregate(fns...) +} + +func (cliq *CartLineItemsQuery) prepareQuery(ctx context.Context) error { + for _, inter := range cliq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, cliq); err != nil { + return err + } + } + } + for _, f := range cliq.ctx.Fields { + if !cartlineitems.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if cliq.path != nil { + prev, err := cliq.path(ctx) + if err != nil { + return err + } + cliq.sql = prev + } + return nil +} + +func (cliq *CartLineItemsQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*CartLineItems, error) { + var ( + nodes = []*CartLineItems{} + _spec = cliq.querySpec() + loadedTypes = [1]bool{ + cliq.withCart != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*CartLineItems).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &CartLineItems{config: cliq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, cliq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := cliq.withCart; query != nil { + if err := cliq.loadCart(ctx, query, nodes, nil, + func(n *CartLineItems, e *Cart) { n.Edges.Cart = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (cliq *CartLineItemsQuery) loadCart(ctx context.Context, query *CartQuery, nodes []*CartLineItems, init func(*CartLineItems), assign func(*CartLineItems, *Cart)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*CartLineItems) + for i := range nodes { + fk := nodes[i].CartID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(cart.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "cart_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (cliq *CartLineItemsQuery) sqlCount(ctx context.Context) (int, error) { + _spec := cliq.querySpec() + _spec.Node.Columns = cliq.ctx.Fields + if len(cliq.ctx.Fields) > 0 { + _spec.Unique = cliq.ctx.Unique != nil && *cliq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, cliq.driver, _spec) +} + +func (cliq *CartLineItemsQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(cartlineitems.Table, cartlineitems.Columns, sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString)) + _spec.From = cliq.sql + if unique := cliq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if cliq.path != nil { + _spec.Unique = true + } + if fields := cliq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, cartlineitems.FieldID) + for i := range fields { + if fields[i] != cartlineitems.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if cliq.withCart != nil { + _spec.Node.AddColumnOnce(cartlineitems.FieldCartID) + } + } + if ps := cliq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := cliq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := cliq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := cliq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (cliq *CartLineItemsQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(cliq.driver.Dialect()) + t1 := builder.Table(cartlineitems.Table) + columns := cliq.ctx.Fields + if len(columns) == 0 { + columns = cartlineitems.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if cliq.sql != nil { + selector = cliq.sql + selector.Select(selector.Columns(columns...)...) + } + if cliq.ctx.Unique != nil && *cliq.ctx.Unique { + selector.Distinct() + } + for _, p := range cliq.predicates { + p(selector) + } + for _, p := range cliq.order { + p(selector) + } + if offset := cliq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := cliq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// CartLineItemsGroupBy is the group-by builder for CartLineItems entities. +type CartLineItemsGroupBy struct { + selector + build *CartLineItemsQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (cligb *CartLineItemsGroupBy) Aggregate(fns ...AggregateFunc) *CartLineItemsGroupBy { + cligb.fns = append(cligb.fns, fns...) + return cligb +} + +// Scan applies the selector query and scans the result into the given value. +func (cligb *CartLineItemsGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, cligb.build.ctx, ent.OpQueryGroupBy) + if err := cligb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*CartLineItemsQuery, *CartLineItemsGroupBy](ctx, cligb.build, cligb, cligb.build.inters, v) +} + +func (cligb *CartLineItemsGroupBy) sqlScan(ctx context.Context, root *CartLineItemsQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(cligb.fns)) + for _, fn := range cligb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*cligb.flds)+len(cligb.fns)) + for _, f := range *cligb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*cligb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := cligb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// CartLineItemsSelect is the builder for selecting fields of CartLineItems entities. +type CartLineItemsSelect struct { + *CartLineItemsQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (clis *CartLineItemsSelect) Aggregate(fns ...AggregateFunc) *CartLineItemsSelect { + clis.fns = append(clis.fns, fns...) + return clis +} + +// Scan applies the selector query and scans the result into the given value. +func (clis *CartLineItemsSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, clis.ctx, ent.OpQuerySelect) + if err := clis.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*CartLineItemsQuery, *CartLineItemsSelect](ctx, clis.CartLineItemsQuery, clis, clis.inters, v) +} + +func (clis *CartLineItemsSelect) sqlScan(ctx context.Context, root *CartLineItemsQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(clis.fns)) + for _, fn := range clis.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*clis.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := clis.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/cartlineitems_update.go b/ent/cartlineitems_update.go new file mode 100644 index 0000000..ec3f114 --- /dev/null +++ b/ent/cartlineitems_update.go @@ -0,0 +1,587 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cartlineitems" + "github.com/omkar273/codegeeky/ent/predicate" + "github.com/shopspring/decimal" +) + +// CartLineItemsUpdate is the builder for updating CartLineItems entities. +type CartLineItemsUpdate struct { + config + hooks []Hook + mutation *CartLineItemsMutation +} + +// Where appends a list predicates to the CartLineItemsUpdate builder. +func (cliu *CartLineItemsUpdate) Where(ps ...predicate.CartLineItems) *CartLineItemsUpdate { + cliu.mutation.Where(ps...) + return cliu +} + +// SetStatus sets the "status" field. +func (cliu *CartLineItemsUpdate) SetStatus(s string) *CartLineItemsUpdate { + cliu.mutation.SetStatus(s) + return cliu +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (cliu *CartLineItemsUpdate) SetNillableStatus(s *string) *CartLineItemsUpdate { + if s != nil { + cliu.SetStatus(*s) + } + return cliu +} + +// SetUpdatedAt sets the "updated_at" field. +func (cliu *CartLineItemsUpdate) SetUpdatedAt(t time.Time) *CartLineItemsUpdate { + cliu.mutation.SetUpdatedAt(t) + return cliu +} + +// SetUpdatedBy sets the "updated_by" field. +func (cliu *CartLineItemsUpdate) SetUpdatedBy(s string) *CartLineItemsUpdate { + cliu.mutation.SetUpdatedBy(s) + return cliu +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (cliu *CartLineItemsUpdate) SetNillableUpdatedBy(s *string) *CartLineItemsUpdate { + if s != nil { + cliu.SetUpdatedBy(*s) + } + return cliu +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (cliu *CartLineItemsUpdate) ClearUpdatedBy() *CartLineItemsUpdate { + cliu.mutation.ClearUpdatedBy() + return cliu +} + +// SetMetadata sets the "metadata" field. +func (cliu *CartLineItemsUpdate) SetMetadata(m map[string]string) *CartLineItemsUpdate { + cliu.mutation.SetMetadata(m) + return cliu +} + +// ClearMetadata clears the value of the "metadata" field. +func (cliu *CartLineItemsUpdate) ClearMetadata() *CartLineItemsUpdate { + cliu.mutation.ClearMetadata() + return cliu +} + +// SetQuantity sets the "quantity" field. +func (cliu *CartLineItemsUpdate) SetQuantity(i int) *CartLineItemsUpdate { + cliu.mutation.ResetQuantity() + cliu.mutation.SetQuantity(i) + return cliu +} + +// SetNillableQuantity sets the "quantity" field if the given value is not nil. +func (cliu *CartLineItemsUpdate) SetNillableQuantity(i *int) *CartLineItemsUpdate { + if i != nil { + cliu.SetQuantity(*i) + } + return cliu +} + +// AddQuantity adds i to the "quantity" field. +func (cliu *CartLineItemsUpdate) AddQuantity(i int) *CartLineItemsUpdate { + cliu.mutation.AddQuantity(i) + return cliu +} + +// SetPerUnitPrice sets the "per_unit_price" field. +func (cliu *CartLineItemsUpdate) SetPerUnitPrice(d decimal.Decimal) *CartLineItemsUpdate { + cliu.mutation.SetPerUnitPrice(d) + return cliu +} + +// SetNillablePerUnitPrice sets the "per_unit_price" field if the given value is not nil. +func (cliu *CartLineItemsUpdate) SetNillablePerUnitPrice(d *decimal.Decimal) *CartLineItemsUpdate { + if d != nil { + cliu.SetPerUnitPrice(*d) + } + return cliu +} + +// SetTaxAmount sets the "tax_amount" field. +func (cliu *CartLineItemsUpdate) SetTaxAmount(d decimal.Decimal) *CartLineItemsUpdate { + cliu.mutation.SetTaxAmount(d) + return cliu +} + +// SetNillableTaxAmount sets the "tax_amount" field if the given value is not nil. +func (cliu *CartLineItemsUpdate) SetNillableTaxAmount(d *decimal.Decimal) *CartLineItemsUpdate { + if d != nil { + cliu.SetTaxAmount(*d) + } + return cliu +} + +// SetDiscountAmount sets the "discount_amount" field. +func (cliu *CartLineItemsUpdate) SetDiscountAmount(d decimal.Decimal) *CartLineItemsUpdate { + cliu.mutation.SetDiscountAmount(d) + return cliu +} + +// SetNillableDiscountAmount sets the "discount_amount" field if the given value is not nil. +func (cliu *CartLineItemsUpdate) SetNillableDiscountAmount(d *decimal.Decimal) *CartLineItemsUpdate { + if d != nil { + cliu.SetDiscountAmount(*d) + } + return cliu +} + +// SetSubtotal sets the "subtotal" field. +func (cliu *CartLineItemsUpdate) SetSubtotal(d decimal.Decimal) *CartLineItemsUpdate { + cliu.mutation.SetSubtotal(d) + return cliu +} + +// SetNillableSubtotal sets the "subtotal" field if the given value is not nil. +func (cliu *CartLineItemsUpdate) SetNillableSubtotal(d *decimal.Decimal) *CartLineItemsUpdate { + if d != nil { + cliu.SetSubtotal(*d) + } + return cliu +} + +// SetTotal sets the "total" field. +func (cliu *CartLineItemsUpdate) SetTotal(d decimal.Decimal) *CartLineItemsUpdate { + cliu.mutation.SetTotal(d) + return cliu +} + +// SetNillableTotal sets the "total" field if the given value is not nil. +func (cliu *CartLineItemsUpdate) SetNillableTotal(d *decimal.Decimal) *CartLineItemsUpdate { + if d != nil { + cliu.SetTotal(*d) + } + return cliu +} + +// Mutation returns the CartLineItemsMutation object of the builder. +func (cliu *CartLineItemsUpdate) Mutation() *CartLineItemsMutation { + return cliu.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (cliu *CartLineItemsUpdate) Save(ctx context.Context) (int, error) { + cliu.defaults() + return withHooks(ctx, cliu.sqlSave, cliu.mutation, cliu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (cliu *CartLineItemsUpdate) SaveX(ctx context.Context) int { + affected, err := cliu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (cliu *CartLineItemsUpdate) Exec(ctx context.Context) error { + _, err := cliu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (cliu *CartLineItemsUpdate) ExecX(ctx context.Context) { + if err := cliu.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (cliu *CartLineItemsUpdate) defaults() { + if _, ok := cliu.mutation.UpdatedAt(); !ok { + v := cartlineitems.UpdateDefaultUpdatedAt() + cliu.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (cliu *CartLineItemsUpdate) check() error { + if cliu.mutation.CartCleared() && len(cliu.mutation.CartIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "CartLineItems.cart"`) + } + return nil +} + +func (cliu *CartLineItemsUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := cliu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(cartlineitems.Table, cartlineitems.Columns, sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString)) + if ps := cliu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := cliu.mutation.Status(); ok { + _spec.SetField(cartlineitems.FieldStatus, field.TypeString, value) + } + if value, ok := cliu.mutation.UpdatedAt(); ok { + _spec.SetField(cartlineitems.FieldUpdatedAt, field.TypeTime, value) + } + if cliu.mutation.CreatedByCleared() { + _spec.ClearField(cartlineitems.FieldCreatedBy, field.TypeString) + } + if value, ok := cliu.mutation.UpdatedBy(); ok { + _spec.SetField(cartlineitems.FieldUpdatedBy, field.TypeString, value) + } + if cliu.mutation.UpdatedByCleared() { + _spec.ClearField(cartlineitems.FieldUpdatedBy, field.TypeString) + } + if value, ok := cliu.mutation.Metadata(); ok { + _spec.SetField(cartlineitems.FieldMetadata, field.TypeJSON, value) + } + if cliu.mutation.MetadataCleared() { + _spec.ClearField(cartlineitems.FieldMetadata, field.TypeJSON) + } + if value, ok := cliu.mutation.Quantity(); ok { + _spec.SetField(cartlineitems.FieldQuantity, field.TypeInt, value) + } + if value, ok := cliu.mutation.AddedQuantity(); ok { + _spec.AddField(cartlineitems.FieldQuantity, field.TypeInt, value) + } + if value, ok := cliu.mutation.PerUnitPrice(); ok { + _spec.SetField(cartlineitems.FieldPerUnitPrice, field.TypeOther, value) + } + if value, ok := cliu.mutation.TaxAmount(); ok { + _spec.SetField(cartlineitems.FieldTaxAmount, field.TypeOther, value) + } + if value, ok := cliu.mutation.DiscountAmount(); ok { + _spec.SetField(cartlineitems.FieldDiscountAmount, field.TypeOther, value) + } + if value, ok := cliu.mutation.Subtotal(); ok { + _spec.SetField(cartlineitems.FieldSubtotal, field.TypeOther, value) + } + if value, ok := cliu.mutation.Total(); ok { + _spec.SetField(cartlineitems.FieldTotal, field.TypeOther, value) + } + if n, err = sqlgraph.UpdateNodes(ctx, cliu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{cartlineitems.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + cliu.mutation.done = true + return n, nil +} + +// CartLineItemsUpdateOne is the builder for updating a single CartLineItems entity. +type CartLineItemsUpdateOne struct { + config + fields []string + hooks []Hook + mutation *CartLineItemsMutation +} + +// SetStatus sets the "status" field. +func (cliuo *CartLineItemsUpdateOne) SetStatus(s string) *CartLineItemsUpdateOne { + cliuo.mutation.SetStatus(s) + return cliuo +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (cliuo *CartLineItemsUpdateOne) SetNillableStatus(s *string) *CartLineItemsUpdateOne { + if s != nil { + cliuo.SetStatus(*s) + } + return cliuo +} + +// SetUpdatedAt sets the "updated_at" field. +func (cliuo *CartLineItemsUpdateOne) SetUpdatedAt(t time.Time) *CartLineItemsUpdateOne { + cliuo.mutation.SetUpdatedAt(t) + return cliuo +} + +// SetUpdatedBy sets the "updated_by" field. +func (cliuo *CartLineItemsUpdateOne) SetUpdatedBy(s string) *CartLineItemsUpdateOne { + cliuo.mutation.SetUpdatedBy(s) + return cliuo +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (cliuo *CartLineItemsUpdateOne) SetNillableUpdatedBy(s *string) *CartLineItemsUpdateOne { + if s != nil { + cliuo.SetUpdatedBy(*s) + } + return cliuo +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (cliuo *CartLineItemsUpdateOne) ClearUpdatedBy() *CartLineItemsUpdateOne { + cliuo.mutation.ClearUpdatedBy() + return cliuo +} + +// SetMetadata sets the "metadata" field. +func (cliuo *CartLineItemsUpdateOne) SetMetadata(m map[string]string) *CartLineItemsUpdateOne { + cliuo.mutation.SetMetadata(m) + return cliuo +} + +// ClearMetadata clears the value of the "metadata" field. +func (cliuo *CartLineItemsUpdateOne) ClearMetadata() *CartLineItemsUpdateOne { + cliuo.mutation.ClearMetadata() + return cliuo +} + +// SetQuantity sets the "quantity" field. +func (cliuo *CartLineItemsUpdateOne) SetQuantity(i int) *CartLineItemsUpdateOne { + cliuo.mutation.ResetQuantity() + cliuo.mutation.SetQuantity(i) + return cliuo +} + +// SetNillableQuantity sets the "quantity" field if the given value is not nil. +func (cliuo *CartLineItemsUpdateOne) SetNillableQuantity(i *int) *CartLineItemsUpdateOne { + if i != nil { + cliuo.SetQuantity(*i) + } + return cliuo +} + +// AddQuantity adds i to the "quantity" field. +func (cliuo *CartLineItemsUpdateOne) AddQuantity(i int) *CartLineItemsUpdateOne { + cliuo.mutation.AddQuantity(i) + return cliuo +} + +// SetPerUnitPrice sets the "per_unit_price" field. +func (cliuo *CartLineItemsUpdateOne) SetPerUnitPrice(d decimal.Decimal) *CartLineItemsUpdateOne { + cliuo.mutation.SetPerUnitPrice(d) + return cliuo +} + +// SetNillablePerUnitPrice sets the "per_unit_price" field if the given value is not nil. +func (cliuo *CartLineItemsUpdateOne) SetNillablePerUnitPrice(d *decimal.Decimal) *CartLineItemsUpdateOne { + if d != nil { + cliuo.SetPerUnitPrice(*d) + } + return cliuo +} + +// SetTaxAmount sets the "tax_amount" field. +func (cliuo *CartLineItemsUpdateOne) SetTaxAmount(d decimal.Decimal) *CartLineItemsUpdateOne { + cliuo.mutation.SetTaxAmount(d) + return cliuo +} + +// SetNillableTaxAmount sets the "tax_amount" field if the given value is not nil. +func (cliuo *CartLineItemsUpdateOne) SetNillableTaxAmount(d *decimal.Decimal) *CartLineItemsUpdateOne { + if d != nil { + cliuo.SetTaxAmount(*d) + } + return cliuo +} + +// SetDiscountAmount sets the "discount_amount" field. +func (cliuo *CartLineItemsUpdateOne) SetDiscountAmount(d decimal.Decimal) *CartLineItemsUpdateOne { + cliuo.mutation.SetDiscountAmount(d) + return cliuo +} + +// SetNillableDiscountAmount sets the "discount_amount" field if the given value is not nil. +func (cliuo *CartLineItemsUpdateOne) SetNillableDiscountAmount(d *decimal.Decimal) *CartLineItemsUpdateOne { + if d != nil { + cliuo.SetDiscountAmount(*d) + } + return cliuo +} + +// SetSubtotal sets the "subtotal" field. +func (cliuo *CartLineItemsUpdateOne) SetSubtotal(d decimal.Decimal) *CartLineItemsUpdateOne { + cliuo.mutation.SetSubtotal(d) + return cliuo +} + +// SetNillableSubtotal sets the "subtotal" field if the given value is not nil. +func (cliuo *CartLineItemsUpdateOne) SetNillableSubtotal(d *decimal.Decimal) *CartLineItemsUpdateOne { + if d != nil { + cliuo.SetSubtotal(*d) + } + return cliuo +} + +// SetTotal sets the "total" field. +func (cliuo *CartLineItemsUpdateOne) SetTotal(d decimal.Decimal) *CartLineItemsUpdateOne { + cliuo.mutation.SetTotal(d) + return cliuo +} + +// SetNillableTotal sets the "total" field if the given value is not nil. +func (cliuo *CartLineItemsUpdateOne) SetNillableTotal(d *decimal.Decimal) *CartLineItemsUpdateOne { + if d != nil { + cliuo.SetTotal(*d) + } + return cliuo +} + +// Mutation returns the CartLineItemsMutation object of the builder. +func (cliuo *CartLineItemsUpdateOne) Mutation() *CartLineItemsMutation { + return cliuo.mutation +} + +// Where appends a list predicates to the CartLineItemsUpdate builder. +func (cliuo *CartLineItemsUpdateOne) Where(ps ...predicate.CartLineItems) *CartLineItemsUpdateOne { + cliuo.mutation.Where(ps...) + return cliuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (cliuo *CartLineItemsUpdateOne) Select(field string, fields ...string) *CartLineItemsUpdateOne { + cliuo.fields = append([]string{field}, fields...) + return cliuo +} + +// Save executes the query and returns the updated CartLineItems entity. +func (cliuo *CartLineItemsUpdateOne) Save(ctx context.Context) (*CartLineItems, error) { + cliuo.defaults() + return withHooks(ctx, cliuo.sqlSave, cliuo.mutation, cliuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (cliuo *CartLineItemsUpdateOne) SaveX(ctx context.Context) *CartLineItems { + node, err := cliuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (cliuo *CartLineItemsUpdateOne) Exec(ctx context.Context) error { + _, err := cliuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (cliuo *CartLineItemsUpdateOne) ExecX(ctx context.Context) { + if err := cliuo.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (cliuo *CartLineItemsUpdateOne) defaults() { + if _, ok := cliuo.mutation.UpdatedAt(); !ok { + v := cartlineitems.UpdateDefaultUpdatedAt() + cliuo.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (cliuo *CartLineItemsUpdateOne) check() error { + if cliuo.mutation.CartCleared() && len(cliuo.mutation.CartIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "CartLineItems.cart"`) + } + return nil +} + +func (cliuo *CartLineItemsUpdateOne) sqlSave(ctx context.Context) (_node *CartLineItems, err error) { + if err := cliuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(cartlineitems.Table, cartlineitems.Columns, sqlgraph.NewFieldSpec(cartlineitems.FieldID, field.TypeString)) + id, ok := cliuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "CartLineItems.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := cliuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, cartlineitems.FieldID) + for _, f := range fields { + if !cartlineitems.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != cartlineitems.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := cliuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := cliuo.mutation.Status(); ok { + _spec.SetField(cartlineitems.FieldStatus, field.TypeString, value) + } + if value, ok := cliuo.mutation.UpdatedAt(); ok { + _spec.SetField(cartlineitems.FieldUpdatedAt, field.TypeTime, value) + } + if cliuo.mutation.CreatedByCleared() { + _spec.ClearField(cartlineitems.FieldCreatedBy, field.TypeString) + } + if value, ok := cliuo.mutation.UpdatedBy(); ok { + _spec.SetField(cartlineitems.FieldUpdatedBy, field.TypeString, value) + } + if cliuo.mutation.UpdatedByCleared() { + _spec.ClearField(cartlineitems.FieldUpdatedBy, field.TypeString) + } + if value, ok := cliuo.mutation.Metadata(); ok { + _spec.SetField(cartlineitems.FieldMetadata, field.TypeJSON, value) + } + if cliuo.mutation.MetadataCleared() { + _spec.ClearField(cartlineitems.FieldMetadata, field.TypeJSON) + } + if value, ok := cliuo.mutation.Quantity(); ok { + _spec.SetField(cartlineitems.FieldQuantity, field.TypeInt, value) + } + if value, ok := cliuo.mutation.AddedQuantity(); ok { + _spec.AddField(cartlineitems.FieldQuantity, field.TypeInt, value) + } + if value, ok := cliuo.mutation.PerUnitPrice(); ok { + _spec.SetField(cartlineitems.FieldPerUnitPrice, field.TypeOther, value) + } + if value, ok := cliuo.mutation.TaxAmount(); ok { + _spec.SetField(cartlineitems.FieldTaxAmount, field.TypeOther, value) + } + if value, ok := cliuo.mutation.DiscountAmount(); ok { + _spec.SetField(cartlineitems.FieldDiscountAmount, field.TypeOther, value) + } + if value, ok := cliuo.mutation.Subtotal(); ok { + _spec.SetField(cartlineitems.FieldSubtotal, field.TypeOther, value) + } + if value, ok := cliuo.mutation.Total(); ok { + _spec.SetField(cartlineitems.FieldTotal, field.TypeOther, value) + } + _node = &CartLineItems{config: cliuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, cliuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{cartlineitems.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + cliuo.mutation.done = true + return _node, nil +} diff --git a/ent/category.go b/ent/category.go index cff9ac0..15c2ff5 100644 --- a/ent/category.go +++ b/ent/category.go @@ -3,6 +3,7 @@ package ent import ( + "encoding/json" "fmt" "strings" "time" @@ -27,6 +28,8 @@ type Category struct { CreatedBy string `json:"created_by,omitempty"` // UpdatedBy holds the value of the "updated_by" field. UpdatedBy string `json:"updated_by,omitempty"` + // Metadata holds the value of the "metadata" field. + Metadata map[string]string `json:"metadata,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` // LookupKey holds the value of the "lookup_key" field. @@ -63,6 +66,8 @@ func (*Category) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { + case category.FieldMetadata: + values[i] = new([]byte) case category.FieldID, category.FieldStatus, category.FieldCreatedBy, category.FieldUpdatedBy, category.FieldName, category.FieldLookupKey, category.FieldDescription: values[i] = new(sql.NullString) case category.FieldCreatedAt, category.FieldUpdatedAt: @@ -120,6 +125,14 @@ func (c *Category) assignValues(columns []string, values []any) error { } else if value.Valid { c.UpdatedBy = value.String } + case category.FieldMetadata: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field metadata", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &c.Metadata); err != nil { + return fmt.Errorf("unmarshal field metadata: %w", err) + } + } case category.FieldName: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field name", values[i]) @@ -201,6 +214,9 @@ func (c *Category) String() string { builder.WriteString("updated_by=") builder.WriteString(c.UpdatedBy) builder.WriteString(", ") + builder.WriteString("metadata=") + builder.WriteString(fmt.Sprintf("%v", c.Metadata)) + builder.WriteString(", ") builder.WriteString("name=") builder.WriteString(c.Name) builder.WriteString(", ") diff --git a/ent/category/category.go b/ent/category/category.go index 57ae9ec..4f41926 100644 --- a/ent/category/category.go +++ b/ent/category/category.go @@ -24,6 +24,8 @@ const ( FieldCreatedBy = "created_by" // FieldUpdatedBy holds the string denoting the updated_by field in the database. FieldUpdatedBy = "updated_by" + // FieldMetadata holds the string denoting the metadata field in the database. + FieldMetadata = "metadata" // FieldName holds the string denoting the name field in the database. FieldName = "name" // FieldLookupKey holds the string denoting the lookup_key field in the database. @@ -51,6 +53,7 @@ var Columns = []string{ FieldUpdatedAt, FieldCreatedBy, FieldUpdatedBy, + FieldMetadata, FieldName, FieldLookupKey, FieldDescription, @@ -86,6 +89,8 @@ var ( DefaultUpdatedAt func() time.Time // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. UpdateDefaultUpdatedAt func() time.Time + // DefaultMetadata holds the default value on creation for the "metadata" field. + DefaultMetadata map[string]string // NameValidator is a validator for the "name" field. It is called by the builders before save. NameValidator func(string) error // LookupKeyValidator is a validator for the "lookup_key" field. It is called by the builders before save. diff --git a/ent/category/where.go b/ent/category/where.go index 3803e75..04d9f59 100644 --- a/ent/category/where.go +++ b/ent/category/where.go @@ -400,6 +400,16 @@ func UpdatedByContainsFold(v string) predicate.Category { return predicate.Category(sql.FieldContainsFold(FieldUpdatedBy, v)) } +// MetadataIsNil applies the IsNil predicate on the "metadata" field. +func MetadataIsNil() predicate.Category { + return predicate.Category(sql.FieldIsNull(FieldMetadata)) +} + +// MetadataNotNil applies the NotNil predicate on the "metadata" field. +func MetadataNotNil() predicate.Category { + return predicate.Category(sql.FieldNotNull(FieldMetadata)) +} + // NameEQ applies the EQ predicate on the "name" field. func NameEQ(v string) predicate.Category { return predicate.Category(sql.FieldEQ(FieldName, v)) diff --git a/ent/category_create.go b/ent/category_create.go index 4e016fb..15c14c5 100644 --- a/ent/category_create.go +++ b/ent/category_create.go @@ -91,6 +91,12 @@ func (cc *CategoryCreate) SetNillableUpdatedBy(s *string) *CategoryCreate { return cc } +// SetMetadata sets the "metadata" field. +func (cc *CategoryCreate) SetMetadata(m map[string]string) *CategoryCreate { + cc.mutation.SetMetadata(m) + return cc +} + // SetName sets the "name" field. func (cc *CategoryCreate) SetName(s string) *CategoryCreate { cc.mutation.SetName(s) @@ -193,6 +199,10 @@ func (cc *CategoryCreate) defaults() { v := category.DefaultUpdatedAt() cc.mutation.SetUpdatedAt(v) } + if _, ok := cc.mutation.Metadata(); !ok { + v := category.DefaultMetadata + cc.mutation.SetMetadata(v) + } if _, ok := cc.mutation.ID(); !ok { v := category.DefaultID() cc.mutation.SetID(v) @@ -281,6 +291,10 @@ func (cc *CategoryCreate) createSpec() (*Category, *sqlgraph.CreateSpec) { _spec.SetField(category.FieldUpdatedBy, field.TypeString, value) _node.UpdatedBy = value } + if value, ok := cc.mutation.Metadata(); ok { + _spec.SetField(category.FieldMetadata, field.TypeJSON, value) + _node.Metadata = value + } if value, ok := cc.mutation.Name(); ok { _spec.SetField(category.FieldName, field.TypeString, value) _node.Name = value diff --git a/ent/category_update.go b/ent/category_update.go index 3276c25..e570fe5 100644 --- a/ent/category_update.go +++ b/ent/category_update.go @@ -69,6 +69,18 @@ func (cu *CategoryUpdate) ClearUpdatedBy() *CategoryUpdate { return cu } +// SetMetadata sets the "metadata" field. +func (cu *CategoryUpdate) SetMetadata(m map[string]string) *CategoryUpdate { + cu.mutation.SetMetadata(m) + return cu +} + +// ClearMetadata clears the value of the "metadata" field. +func (cu *CategoryUpdate) ClearMetadata() *CategoryUpdate { + cu.mutation.ClearMetadata() + return cu +} + // SetName sets the "name" field. func (cu *CategoryUpdate) SetName(s string) *CategoryUpdate { cu.mutation.SetName(s) @@ -236,6 +248,12 @@ func (cu *CategoryUpdate) sqlSave(ctx context.Context) (n int, err error) { if cu.mutation.UpdatedByCleared() { _spec.ClearField(category.FieldUpdatedBy, field.TypeString) } + if value, ok := cu.mutation.Metadata(); ok { + _spec.SetField(category.FieldMetadata, field.TypeJSON, value) + } + if cu.mutation.MetadataCleared() { + _spec.ClearField(category.FieldMetadata, field.TypeJSON) + } if value, ok := cu.mutation.Name(); ok { _spec.SetField(category.FieldName, field.TypeString, value) } @@ -353,6 +371,18 @@ func (cuo *CategoryUpdateOne) ClearUpdatedBy() *CategoryUpdateOne { return cuo } +// SetMetadata sets the "metadata" field. +func (cuo *CategoryUpdateOne) SetMetadata(m map[string]string) *CategoryUpdateOne { + cuo.mutation.SetMetadata(m) + return cuo +} + +// ClearMetadata clears the value of the "metadata" field. +func (cuo *CategoryUpdateOne) ClearMetadata() *CategoryUpdateOne { + cuo.mutation.ClearMetadata() + return cuo +} + // SetName sets the "name" field. func (cuo *CategoryUpdateOne) SetName(s string) *CategoryUpdateOne { cuo.mutation.SetName(s) @@ -550,6 +580,12 @@ func (cuo *CategoryUpdateOne) sqlSave(ctx context.Context) (_node *Category, err if cuo.mutation.UpdatedByCleared() { _spec.ClearField(category.FieldUpdatedBy, field.TypeString) } + if value, ok := cuo.mutation.Metadata(); ok { + _spec.SetField(category.FieldMetadata, field.TypeJSON, value) + } + if cuo.mutation.MetadataCleared() { + _spec.ClearField(category.FieldMetadata, field.TypeJSON) + } if value, ok := cuo.mutation.Name(); ok { _spec.SetField(category.FieldName, field.TypeString, value) } diff --git a/ent/client.go b/ent/client.go index 30318cf..d1c58f7 100644 --- a/ent/client.go +++ b/ent/client.go @@ -15,10 +15,15 @@ import ( "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" "github.com/omkar273/codegeeky/ent/category" "github.com/omkar273/codegeeky/ent/discount" "github.com/omkar273/codegeeky/ent/fileupload" "github.com/omkar273/codegeeky/ent/internship" + "github.com/omkar273/codegeeky/ent/internshipbatch" + "github.com/omkar273/codegeeky/ent/internshipenrollment" + "github.com/omkar273/codegeeky/ent/order" "github.com/omkar273/codegeeky/ent/payment" "github.com/omkar273/codegeeky/ent/paymentattempt" "github.com/omkar273/codegeeky/ent/user" @@ -29,6 +34,10 @@ type Client struct { config // Schema is the client for creating, migrating and dropping schema. Schema *migrate.Schema + // Cart is the client for interacting with the Cart builders. + Cart *CartClient + // CartLineItems is the client for interacting with the CartLineItems builders. + CartLineItems *CartLineItemsClient // Category is the client for interacting with the Category builders. Category *CategoryClient // Discount is the client for interacting with the Discount builders. @@ -37,6 +46,12 @@ type Client struct { FileUpload *FileUploadClient // Internship is the client for interacting with the Internship builders. Internship *InternshipClient + // InternshipBatch is the client for interacting with the InternshipBatch builders. + InternshipBatch *InternshipBatchClient + // InternshipEnrollment is the client for interacting with the InternshipEnrollment builders. + InternshipEnrollment *InternshipEnrollmentClient + // Order is the client for interacting with the Order builders. + Order *OrderClient // Payment is the client for interacting with the Payment builders. Payment *PaymentClient // PaymentAttempt is the client for interacting with the PaymentAttempt builders. @@ -54,10 +69,15 @@ func NewClient(opts ...Option) *Client { func (c *Client) init() { c.Schema = migrate.NewSchema(c.driver) + c.Cart = NewCartClient(c.config) + c.CartLineItems = NewCartLineItemsClient(c.config) c.Category = NewCategoryClient(c.config) c.Discount = NewDiscountClient(c.config) c.FileUpload = NewFileUploadClient(c.config) c.Internship = NewInternshipClient(c.config) + c.InternshipBatch = NewInternshipBatchClient(c.config) + c.InternshipEnrollment = NewInternshipEnrollmentClient(c.config) + c.Order = NewOrderClient(c.config) c.Payment = NewPaymentClient(c.config) c.PaymentAttempt = NewPaymentAttemptClient(c.config) c.User = NewUserClient(c.config) @@ -151,15 +171,20 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { cfg := c.config cfg.driver = tx return &Tx{ - ctx: ctx, - config: cfg, - Category: NewCategoryClient(cfg), - Discount: NewDiscountClient(cfg), - FileUpload: NewFileUploadClient(cfg), - Internship: NewInternshipClient(cfg), - Payment: NewPaymentClient(cfg), - PaymentAttempt: NewPaymentAttemptClient(cfg), - User: NewUserClient(cfg), + ctx: ctx, + config: cfg, + Cart: NewCartClient(cfg), + CartLineItems: NewCartLineItemsClient(cfg), + Category: NewCategoryClient(cfg), + Discount: NewDiscountClient(cfg), + FileUpload: NewFileUploadClient(cfg), + Internship: NewInternshipClient(cfg), + InternshipBatch: NewInternshipBatchClient(cfg), + InternshipEnrollment: NewInternshipEnrollmentClient(cfg), + Order: NewOrderClient(cfg), + Payment: NewPaymentClient(cfg), + PaymentAttempt: NewPaymentAttemptClient(cfg), + User: NewUserClient(cfg), }, nil } @@ -177,22 +202,27 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) cfg := c.config cfg.driver = &txDriver{tx: tx, drv: c.driver} return &Tx{ - ctx: ctx, - config: cfg, - Category: NewCategoryClient(cfg), - Discount: NewDiscountClient(cfg), - FileUpload: NewFileUploadClient(cfg), - Internship: NewInternshipClient(cfg), - Payment: NewPaymentClient(cfg), - PaymentAttempt: NewPaymentAttemptClient(cfg), - User: NewUserClient(cfg), + ctx: ctx, + config: cfg, + Cart: NewCartClient(cfg), + CartLineItems: NewCartLineItemsClient(cfg), + Category: NewCategoryClient(cfg), + Discount: NewDiscountClient(cfg), + FileUpload: NewFileUploadClient(cfg), + Internship: NewInternshipClient(cfg), + InternshipBatch: NewInternshipBatchClient(cfg), + InternshipEnrollment: NewInternshipEnrollmentClient(cfg), + Order: NewOrderClient(cfg), + Payment: NewPaymentClient(cfg), + PaymentAttempt: NewPaymentAttemptClient(cfg), + User: NewUserClient(cfg), }, nil } // Debug returns a new debug-client. It's used to get verbose logging on specific operations. // // client.Debug(). -// Category. +// Cart. // Query(). // Count(ctx) func (c *Client) Debug() *Client { @@ -215,8 +245,9 @@ func (c *Client) Close() error { // In order to add hooks to a specific client, call: `client.Node.Use(...)`. func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ - c.Category, c.Discount, c.FileUpload, c.Internship, c.Payment, c.PaymentAttempt, - c.User, + c.Cart, c.CartLineItems, c.Category, c.Discount, c.FileUpload, c.Internship, + c.InternshipBatch, c.InternshipEnrollment, c.Order, c.Payment, + c.PaymentAttempt, c.User, } { n.Use(hooks...) } @@ -226,8 +257,9 @@ func (c *Client) Use(hooks ...Hook) { // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ - c.Category, c.Discount, c.FileUpload, c.Internship, c.Payment, c.PaymentAttempt, - c.User, + c.Cart, c.CartLineItems, c.Category, c.Discount, c.FileUpload, c.Internship, + c.InternshipBatch, c.InternshipEnrollment, c.Order, c.Payment, + c.PaymentAttempt, c.User, } { n.Intercept(interceptors...) } @@ -236,6 +268,10 @@ func (c *Client) Intercept(interceptors ...Interceptor) { // Mutate implements the ent.Mutator interface. func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { switch m := m.(type) { + case *CartMutation: + return c.Cart.mutate(ctx, m) + case *CartLineItemsMutation: + return c.CartLineItems.mutate(ctx, m) case *CategoryMutation: return c.Category.mutate(ctx, m) case *DiscountMutation: @@ -244,6 +280,12 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.FileUpload.mutate(ctx, m) case *InternshipMutation: return c.Internship.mutate(ctx, m) + case *InternshipBatchMutation: + return c.InternshipBatch.mutate(ctx, m) + case *InternshipEnrollmentMutation: + return c.InternshipEnrollment.mutate(ctx, m) + case *OrderMutation: + return c.Order.mutate(ctx, m) case *PaymentMutation: return c.Payment.mutate(ctx, m) case *PaymentAttemptMutation: @@ -255,6 +297,320 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { } } +// CartClient is a client for the Cart schema. +type CartClient struct { + config +} + +// NewCartClient returns a client for the Cart from the given config. +func NewCartClient(c config) *CartClient { + return &CartClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `cart.Hooks(f(g(h())))`. +func (c *CartClient) Use(hooks ...Hook) { + c.hooks.Cart = append(c.hooks.Cart, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `cart.Intercept(f(g(h())))`. +func (c *CartClient) Intercept(interceptors ...Interceptor) { + c.inters.Cart = append(c.inters.Cart, interceptors...) +} + +// Create returns a builder for creating a Cart entity. +func (c *CartClient) Create() *CartCreate { + mutation := newCartMutation(c.config, OpCreate) + return &CartCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Cart entities. +func (c *CartClient) CreateBulk(builders ...*CartCreate) *CartCreateBulk { + return &CartCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *CartClient) MapCreateBulk(slice any, setFunc func(*CartCreate, int)) *CartCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &CartCreateBulk{err: fmt.Errorf("calling to CartClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*CartCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &CartCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Cart. +func (c *CartClient) Update() *CartUpdate { + mutation := newCartMutation(c.config, OpUpdate) + return &CartUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *CartClient) UpdateOne(ca *Cart) *CartUpdateOne { + mutation := newCartMutation(c.config, OpUpdateOne, withCart(ca)) + return &CartUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *CartClient) UpdateOneID(id string) *CartUpdateOne { + mutation := newCartMutation(c.config, OpUpdateOne, withCartID(id)) + return &CartUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Cart. +func (c *CartClient) Delete() *CartDelete { + mutation := newCartMutation(c.config, OpDelete) + return &CartDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *CartClient) DeleteOne(ca *Cart) *CartDeleteOne { + return c.DeleteOneID(ca.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *CartClient) DeleteOneID(id string) *CartDeleteOne { + builder := c.Delete().Where(cart.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &CartDeleteOne{builder} +} + +// Query returns a query builder for Cart. +func (c *CartClient) Query() *CartQuery { + return &CartQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeCart}, + inters: c.Interceptors(), + } +} + +// Get returns a Cart entity by its id. +func (c *CartClient) Get(ctx context.Context, id string) (*Cart, error) { + return c.Query().Where(cart.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *CartClient) GetX(ctx context.Context, id string) *Cart { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryLineItems queries the line_items edge of a Cart. +func (c *CartClient) QueryLineItems(ca *Cart) *CartLineItemsQuery { + query := (&CartLineItemsClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := ca.ID + step := sqlgraph.NewStep( + sqlgraph.From(cart.Table, cart.FieldID, id), + sqlgraph.To(cartlineitems.Table, cartlineitems.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, cart.LineItemsTable, cart.LineItemsColumn), + ) + fromV = sqlgraph.Neighbors(ca.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryUser queries the user edge of a Cart. +func (c *CartClient) QueryUser(ca *Cart) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := ca.ID + step := sqlgraph.NewStep( + sqlgraph.From(cart.Table, cart.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, cart.UserTable, cart.UserColumn), + ) + fromV = sqlgraph.Neighbors(ca.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *CartClient) Hooks() []Hook { + return c.hooks.Cart +} + +// Interceptors returns the client interceptors. +func (c *CartClient) Interceptors() []Interceptor { + return c.inters.Cart +} + +func (c *CartClient) mutate(ctx context.Context, m *CartMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&CartCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&CartUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&CartUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&CartDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Cart mutation op: %q", m.Op()) + } +} + +// CartLineItemsClient is a client for the CartLineItems schema. +type CartLineItemsClient struct { + config +} + +// NewCartLineItemsClient returns a client for the CartLineItems from the given config. +func NewCartLineItemsClient(c config) *CartLineItemsClient { + return &CartLineItemsClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `cartlineitems.Hooks(f(g(h())))`. +func (c *CartLineItemsClient) Use(hooks ...Hook) { + c.hooks.CartLineItems = append(c.hooks.CartLineItems, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `cartlineitems.Intercept(f(g(h())))`. +func (c *CartLineItemsClient) Intercept(interceptors ...Interceptor) { + c.inters.CartLineItems = append(c.inters.CartLineItems, interceptors...) +} + +// Create returns a builder for creating a CartLineItems entity. +func (c *CartLineItemsClient) Create() *CartLineItemsCreate { + mutation := newCartLineItemsMutation(c.config, OpCreate) + return &CartLineItemsCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of CartLineItems entities. +func (c *CartLineItemsClient) CreateBulk(builders ...*CartLineItemsCreate) *CartLineItemsCreateBulk { + return &CartLineItemsCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *CartLineItemsClient) MapCreateBulk(slice any, setFunc func(*CartLineItemsCreate, int)) *CartLineItemsCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &CartLineItemsCreateBulk{err: fmt.Errorf("calling to CartLineItemsClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*CartLineItemsCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &CartLineItemsCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for CartLineItems. +func (c *CartLineItemsClient) Update() *CartLineItemsUpdate { + mutation := newCartLineItemsMutation(c.config, OpUpdate) + return &CartLineItemsUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *CartLineItemsClient) UpdateOne(cli *CartLineItems) *CartLineItemsUpdateOne { + mutation := newCartLineItemsMutation(c.config, OpUpdateOne, withCartLineItems(cli)) + return &CartLineItemsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *CartLineItemsClient) UpdateOneID(id string) *CartLineItemsUpdateOne { + mutation := newCartLineItemsMutation(c.config, OpUpdateOne, withCartLineItemsID(id)) + return &CartLineItemsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for CartLineItems. +func (c *CartLineItemsClient) Delete() *CartLineItemsDelete { + mutation := newCartLineItemsMutation(c.config, OpDelete) + return &CartLineItemsDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *CartLineItemsClient) DeleteOne(cli *CartLineItems) *CartLineItemsDeleteOne { + return c.DeleteOneID(cli.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *CartLineItemsClient) DeleteOneID(id string) *CartLineItemsDeleteOne { + builder := c.Delete().Where(cartlineitems.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &CartLineItemsDeleteOne{builder} +} + +// Query returns a query builder for CartLineItems. +func (c *CartLineItemsClient) Query() *CartLineItemsQuery { + return &CartLineItemsQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeCartLineItems}, + inters: c.Interceptors(), + } +} + +// Get returns a CartLineItems entity by its id. +func (c *CartLineItemsClient) Get(ctx context.Context, id string) (*CartLineItems, error) { + return c.Query().Where(cartlineitems.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *CartLineItemsClient) GetX(ctx context.Context, id string) *CartLineItems { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryCart queries the cart edge of a CartLineItems. +func (c *CartLineItemsClient) QueryCart(cli *CartLineItems) *CartQuery { + query := (&CartClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := cli.ID + step := sqlgraph.NewStep( + sqlgraph.From(cartlineitems.Table, cartlineitems.FieldID, id), + sqlgraph.To(cart.Table, cart.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, cartlineitems.CartTable, cartlineitems.CartColumn), + ) + fromV = sqlgraph.Neighbors(cli.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *CartLineItemsClient) Hooks() []Hook { + return c.hooks.CartLineItems +} + +// Interceptors returns the client interceptors. +func (c *CartLineItemsClient) Interceptors() []Interceptor { + return c.inters.CartLineItems +} + +func (c *CartLineItemsClient) mutate(ctx context.Context, m *CartLineItemsMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&CartLineItemsCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&CartLineItemsUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&CartLineItemsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&CartLineItemsDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown CartLineItems mutation op: %q", m.Op()) + } +} + // CategoryClient is a client for the Category schema. type CategoryClient struct { config @@ -819,6 +1175,405 @@ func (c *InternshipClient) mutate(ctx context.Context, m *InternshipMutation) (V } } +// InternshipBatchClient is a client for the InternshipBatch schema. +type InternshipBatchClient struct { + config +} + +// NewInternshipBatchClient returns a client for the InternshipBatch from the given config. +func NewInternshipBatchClient(c config) *InternshipBatchClient { + return &InternshipBatchClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `internshipbatch.Hooks(f(g(h())))`. +func (c *InternshipBatchClient) Use(hooks ...Hook) { + c.hooks.InternshipBatch = append(c.hooks.InternshipBatch, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `internshipbatch.Intercept(f(g(h())))`. +func (c *InternshipBatchClient) Intercept(interceptors ...Interceptor) { + c.inters.InternshipBatch = append(c.inters.InternshipBatch, interceptors...) +} + +// Create returns a builder for creating a InternshipBatch entity. +func (c *InternshipBatchClient) Create() *InternshipBatchCreate { + mutation := newInternshipBatchMutation(c.config, OpCreate) + return &InternshipBatchCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of InternshipBatch entities. +func (c *InternshipBatchClient) CreateBulk(builders ...*InternshipBatchCreate) *InternshipBatchCreateBulk { + return &InternshipBatchCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *InternshipBatchClient) MapCreateBulk(slice any, setFunc func(*InternshipBatchCreate, int)) *InternshipBatchCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &InternshipBatchCreateBulk{err: fmt.Errorf("calling to InternshipBatchClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*InternshipBatchCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &InternshipBatchCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for InternshipBatch. +func (c *InternshipBatchClient) Update() *InternshipBatchUpdate { + mutation := newInternshipBatchMutation(c.config, OpUpdate) + return &InternshipBatchUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *InternshipBatchClient) UpdateOne(ib *InternshipBatch) *InternshipBatchUpdateOne { + mutation := newInternshipBatchMutation(c.config, OpUpdateOne, withInternshipBatch(ib)) + return &InternshipBatchUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *InternshipBatchClient) UpdateOneID(id string) *InternshipBatchUpdateOne { + mutation := newInternshipBatchMutation(c.config, OpUpdateOne, withInternshipBatchID(id)) + return &InternshipBatchUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for InternshipBatch. +func (c *InternshipBatchClient) Delete() *InternshipBatchDelete { + mutation := newInternshipBatchMutation(c.config, OpDelete) + return &InternshipBatchDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *InternshipBatchClient) DeleteOne(ib *InternshipBatch) *InternshipBatchDeleteOne { + return c.DeleteOneID(ib.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *InternshipBatchClient) DeleteOneID(id string) *InternshipBatchDeleteOne { + builder := c.Delete().Where(internshipbatch.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &InternshipBatchDeleteOne{builder} +} + +// Query returns a query builder for InternshipBatch. +func (c *InternshipBatchClient) Query() *InternshipBatchQuery { + return &InternshipBatchQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeInternshipBatch}, + inters: c.Interceptors(), + } +} + +// Get returns a InternshipBatch entity by its id. +func (c *InternshipBatchClient) Get(ctx context.Context, id string) (*InternshipBatch, error) { + return c.Query().Where(internshipbatch.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *InternshipBatchClient) GetX(ctx context.Context, id string) *InternshipBatch { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *InternshipBatchClient) Hooks() []Hook { + return c.hooks.InternshipBatch +} + +// Interceptors returns the client interceptors. +func (c *InternshipBatchClient) Interceptors() []Interceptor { + return c.inters.InternshipBatch +} + +func (c *InternshipBatchClient) mutate(ctx context.Context, m *InternshipBatchMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&InternshipBatchCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&InternshipBatchUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&InternshipBatchUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&InternshipBatchDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown InternshipBatch mutation op: %q", m.Op()) + } +} + +// InternshipEnrollmentClient is a client for the InternshipEnrollment schema. +type InternshipEnrollmentClient struct { + config +} + +// NewInternshipEnrollmentClient returns a client for the InternshipEnrollment from the given config. +func NewInternshipEnrollmentClient(c config) *InternshipEnrollmentClient { + return &InternshipEnrollmentClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `internshipenrollment.Hooks(f(g(h())))`. +func (c *InternshipEnrollmentClient) Use(hooks ...Hook) { + c.hooks.InternshipEnrollment = append(c.hooks.InternshipEnrollment, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `internshipenrollment.Intercept(f(g(h())))`. +func (c *InternshipEnrollmentClient) Intercept(interceptors ...Interceptor) { + c.inters.InternshipEnrollment = append(c.inters.InternshipEnrollment, interceptors...) +} + +// Create returns a builder for creating a InternshipEnrollment entity. +func (c *InternshipEnrollmentClient) Create() *InternshipEnrollmentCreate { + mutation := newInternshipEnrollmentMutation(c.config, OpCreate) + return &InternshipEnrollmentCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of InternshipEnrollment entities. +func (c *InternshipEnrollmentClient) CreateBulk(builders ...*InternshipEnrollmentCreate) *InternshipEnrollmentCreateBulk { + return &InternshipEnrollmentCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *InternshipEnrollmentClient) MapCreateBulk(slice any, setFunc func(*InternshipEnrollmentCreate, int)) *InternshipEnrollmentCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &InternshipEnrollmentCreateBulk{err: fmt.Errorf("calling to InternshipEnrollmentClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*InternshipEnrollmentCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &InternshipEnrollmentCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for InternshipEnrollment. +func (c *InternshipEnrollmentClient) Update() *InternshipEnrollmentUpdate { + mutation := newInternshipEnrollmentMutation(c.config, OpUpdate) + return &InternshipEnrollmentUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *InternshipEnrollmentClient) UpdateOne(ie *InternshipEnrollment) *InternshipEnrollmentUpdateOne { + mutation := newInternshipEnrollmentMutation(c.config, OpUpdateOne, withInternshipEnrollment(ie)) + return &InternshipEnrollmentUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *InternshipEnrollmentClient) UpdateOneID(id string) *InternshipEnrollmentUpdateOne { + mutation := newInternshipEnrollmentMutation(c.config, OpUpdateOne, withInternshipEnrollmentID(id)) + return &InternshipEnrollmentUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for InternshipEnrollment. +func (c *InternshipEnrollmentClient) Delete() *InternshipEnrollmentDelete { + mutation := newInternshipEnrollmentMutation(c.config, OpDelete) + return &InternshipEnrollmentDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *InternshipEnrollmentClient) DeleteOne(ie *InternshipEnrollment) *InternshipEnrollmentDeleteOne { + return c.DeleteOneID(ie.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *InternshipEnrollmentClient) DeleteOneID(id string) *InternshipEnrollmentDeleteOne { + builder := c.Delete().Where(internshipenrollment.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &InternshipEnrollmentDeleteOne{builder} +} + +// Query returns a query builder for InternshipEnrollment. +func (c *InternshipEnrollmentClient) Query() *InternshipEnrollmentQuery { + return &InternshipEnrollmentQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeInternshipEnrollment}, + inters: c.Interceptors(), + } +} + +// Get returns a InternshipEnrollment entity by its id. +func (c *InternshipEnrollmentClient) Get(ctx context.Context, id string) (*InternshipEnrollment, error) { + return c.Query().Where(internshipenrollment.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *InternshipEnrollmentClient) GetX(ctx context.Context, id string) *InternshipEnrollment { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *InternshipEnrollmentClient) Hooks() []Hook { + return c.hooks.InternshipEnrollment +} + +// Interceptors returns the client interceptors. +func (c *InternshipEnrollmentClient) Interceptors() []Interceptor { + return c.inters.InternshipEnrollment +} + +func (c *InternshipEnrollmentClient) mutate(ctx context.Context, m *InternshipEnrollmentMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&InternshipEnrollmentCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&InternshipEnrollmentUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&InternshipEnrollmentUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&InternshipEnrollmentDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown InternshipEnrollment mutation op: %q", m.Op()) + } +} + +// OrderClient is a client for the Order schema. +type OrderClient struct { + config +} + +// NewOrderClient returns a client for the Order from the given config. +func NewOrderClient(c config) *OrderClient { + return &OrderClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `order.Hooks(f(g(h())))`. +func (c *OrderClient) Use(hooks ...Hook) { + c.hooks.Order = append(c.hooks.Order, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `order.Intercept(f(g(h())))`. +func (c *OrderClient) Intercept(interceptors ...Interceptor) { + c.inters.Order = append(c.inters.Order, interceptors...) +} + +// Create returns a builder for creating a Order entity. +func (c *OrderClient) Create() *OrderCreate { + mutation := newOrderMutation(c.config, OpCreate) + return &OrderCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Order entities. +func (c *OrderClient) CreateBulk(builders ...*OrderCreate) *OrderCreateBulk { + return &OrderCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *OrderClient) MapCreateBulk(slice any, setFunc func(*OrderCreate, int)) *OrderCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &OrderCreateBulk{err: fmt.Errorf("calling to OrderClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*OrderCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &OrderCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Order. +func (c *OrderClient) Update() *OrderUpdate { + mutation := newOrderMutation(c.config, OpUpdate) + return &OrderUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *OrderClient) UpdateOne(o *Order) *OrderUpdateOne { + mutation := newOrderMutation(c.config, OpUpdateOne, withOrder(o)) + return &OrderUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *OrderClient) UpdateOneID(id int) *OrderUpdateOne { + mutation := newOrderMutation(c.config, OpUpdateOne, withOrderID(id)) + return &OrderUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Order. +func (c *OrderClient) Delete() *OrderDelete { + mutation := newOrderMutation(c.config, OpDelete) + return &OrderDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *OrderClient) DeleteOne(o *Order) *OrderDeleteOne { + return c.DeleteOneID(o.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *OrderClient) DeleteOneID(id int) *OrderDeleteOne { + builder := c.Delete().Where(order.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &OrderDeleteOne{builder} +} + +// Query returns a query builder for Order. +func (c *OrderClient) Query() *OrderQuery { + return &OrderQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeOrder}, + inters: c.Interceptors(), + } +} + +// Get returns a Order entity by its id. +func (c *OrderClient) Get(ctx context.Context, id int) (*Order, error) { + return c.Query().Where(order.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *OrderClient) GetX(ctx context.Context, id int) *Order { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *OrderClient) Hooks() []Hook { + return c.hooks.Order +} + +// Interceptors returns the client interceptors. +func (c *OrderClient) Interceptors() []Interceptor { + return c.inters.Order +} + +func (c *OrderClient) mutate(ctx context.Context, m *OrderMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&OrderCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&OrderUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&OrderUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&OrderDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Order mutation op: %q", m.Op()) + } +} + // PaymentClient is a client for the Payment schema. type PaymentClient struct { config @@ -1225,6 +1980,22 @@ func (c *UserClient) GetX(ctx context.Context, id string) *User { return obj } +// QueryCarts queries the carts edge of a User. +func (c *UserClient) QueryCarts(u *User) *CartQuery { + query := (&CartClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(cart.Table, cart.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.CartsTable, user.CartsColumn), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *UserClient) Hooks() []Hook { return c.hooks.User @@ -1253,11 +2024,13 @@ func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error) // hooks and interceptors per client, for fast access. type ( hooks struct { - Category, Discount, FileUpload, Internship, Payment, PaymentAttempt, + Cart, CartLineItems, Category, Discount, FileUpload, Internship, + InternshipBatch, InternshipEnrollment, Order, Payment, PaymentAttempt, User []ent.Hook } inters struct { - Category, Discount, FileUpload, Internship, Payment, PaymentAttempt, + Cart, CartLineItems, Category, Discount, FileUpload, Internship, + InternshipBatch, InternshipEnrollment, Order, Payment, PaymentAttempt, User []ent.Interceptor } ) diff --git a/ent/ent.go b/ent/ent.go index 1665b0f..1cf7007 100644 --- a/ent/ent.go +++ b/ent/ent.go @@ -12,10 +12,15 @@ import ( "entgo.io/ent" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" "github.com/omkar273/codegeeky/ent/category" "github.com/omkar273/codegeeky/ent/discount" "github.com/omkar273/codegeeky/ent/fileupload" "github.com/omkar273/codegeeky/ent/internship" + "github.com/omkar273/codegeeky/ent/internshipbatch" + "github.com/omkar273/codegeeky/ent/internshipenrollment" + "github.com/omkar273/codegeeky/ent/order" "github.com/omkar273/codegeeky/ent/payment" "github.com/omkar273/codegeeky/ent/paymentattempt" "github.com/omkar273/codegeeky/ent/user" @@ -79,13 +84,18 @@ var ( func checkColumn(table, column string) error { initCheck.Do(func() { columnCheck = sql.NewColumnCheck(map[string]func(string) bool{ - category.Table: category.ValidColumn, - discount.Table: discount.ValidColumn, - fileupload.Table: fileupload.ValidColumn, - internship.Table: internship.ValidColumn, - payment.Table: payment.ValidColumn, - paymentattempt.Table: paymentattempt.ValidColumn, - user.Table: user.ValidColumn, + cart.Table: cart.ValidColumn, + cartlineitems.Table: cartlineitems.ValidColumn, + category.Table: category.ValidColumn, + discount.Table: discount.ValidColumn, + fileupload.Table: fileupload.ValidColumn, + internship.Table: internship.ValidColumn, + internshipbatch.Table: internshipbatch.ValidColumn, + internshipenrollment.Table: internshipenrollment.ValidColumn, + order.Table: order.ValidColumn, + payment.Table: payment.ValidColumn, + paymentattempt.Table: paymentattempt.ValidColumn, + user.Table: user.ValidColumn, }) }) return columnCheck(table, column) diff --git a/ent/hook/hook.go b/ent/hook/hook.go index 989909f..1a6acfe 100644 --- a/ent/hook/hook.go +++ b/ent/hook/hook.go @@ -9,6 +9,30 @@ import ( "github.com/omkar273/codegeeky/ent" ) +// The CartFunc type is an adapter to allow the use of ordinary +// function as Cart mutator. +type CartFunc func(context.Context, *ent.CartMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f CartFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.CartMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.CartMutation", m) +} + +// The CartLineItemsFunc type is an adapter to allow the use of ordinary +// function as CartLineItems mutator. +type CartLineItemsFunc func(context.Context, *ent.CartLineItemsMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f CartLineItemsFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.CartLineItemsMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.CartLineItemsMutation", m) +} + // The CategoryFunc type is an adapter to allow the use of ordinary // function as Category mutator. type CategoryFunc func(context.Context, *ent.CategoryMutation) (ent.Value, error) @@ -57,6 +81,42 @@ func (f InternshipFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.InternshipMutation", m) } +// The InternshipBatchFunc type is an adapter to allow the use of ordinary +// function as InternshipBatch mutator. +type InternshipBatchFunc func(context.Context, *ent.InternshipBatchMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f InternshipBatchFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.InternshipBatchMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.InternshipBatchMutation", m) +} + +// The InternshipEnrollmentFunc type is an adapter to allow the use of ordinary +// function as InternshipEnrollment mutator. +type InternshipEnrollmentFunc func(context.Context, *ent.InternshipEnrollmentMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f InternshipEnrollmentFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.InternshipEnrollmentMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.InternshipEnrollmentMutation", m) +} + +// The OrderFunc type is an adapter to allow the use of ordinary +// function as Order mutator. +type OrderFunc func(context.Context, *ent.OrderMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f OrderFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.OrderMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.OrderMutation", m) +} + // The PaymentFunc type is an adapter to allow the use of ordinary // function as Payment mutator. type PaymentFunc func(context.Context, *ent.PaymentMutation) (ent.Value, error) diff --git a/ent/internship.go b/ent/internship.go index dd55734..10a4033 100644 --- a/ent/internship.go +++ b/ent/internship.go @@ -54,9 +54,13 @@ type Internship struct { // Price of the internship Price decimal.Decimal `json:"price,omitempty"` // Flat discount on the internship - FlatDiscount decimal.Decimal `json:"flat_discount,omitempty"` + FlatDiscount *decimal.Decimal `json:"flat_discount,omitempty"` // Percentage discount on the internship - PercentageDiscount decimal.Decimal `json:"percentage_discount,omitempty"` + PercentageDiscount *decimal.Decimal `json:"percentage_discount,omitempty"` + // Subtotal of the internship + Subtotal decimal.Decimal `json:"subtotal,omitempty"` + // Price of the internship + Total decimal.Decimal `json:"total,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the InternshipQuery when eager-loading is set. Edges InternshipEdges `json:"edges"` @@ -87,9 +91,11 @@ func (*Internship) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { + case internship.FieldFlatDiscount, internship.FieldPercentageDiscount: + values[i] = &sql.NullScanner{S: new(decimal.Decimal)} case internship.FieldSkills, internship.FieldLearningOutcomes, internship.FieldPrerequisites, internship.FieldBenefits: values[i] = new([]byte) - case internship.FieldPrice, internship.FieldFlatDiscount, internship.FieldPercentageDiscount: + case internship.FieldPrice, internship.FieldSubtotal, internship.FieldTotal: values[i] = new(decimal.Decimal) case internship.FieldDurationInWeeks: values[i] = new(sql.NullInt64) @@ -231,16 +237,30 @@ func (i *Internship) assignValues(columns []string, values []any) error { i.Price = *value } case internship.FieldFlatDiscount: - if value, ok := values[j].(*decimal.Decimal); !ok { + if value, ok := values[j].(*sql.NullScanner); !ok { return fmt.Errorf("unexpected type %T for field flat_discount", values[j]) - } else if value != nil { - i.FlatDiscount = *value + } else if value.Valid { + i.FlatDiscount = new(decimal.Decimal) + *i.FlatDiscount = *value.S.(*decimal.Decimal) } case internship.FieldPercentageDiscount: - if value, ok := values[j].(*decimal.Decimal); !ok { + if value, ok := values[j].(*sql.NullScanner); !ok { return fmt.Errorf("unexpected type %T for field percentage_discount", values[j]) + } else if value.Valid { + i.PercentageDiscount = new(decimal.Decimal) + *i.PercentageDiscount = *value.S.(*decimal.Decimal) + } + case internship.FieldSubtotal: + if value, ok := values[j].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field subtotal", values[j]) } else if value != nil { - i.PercentageDiscount = *value + i.Subtotal = *value + } + case internship.FieldTotal: + if value, ok := values[j].(*decimal.Decimal); !ok { + return fmt.Errorf("unexpected type %T for field total", values[j]) + } else if value != nil { + i.Total = *value } case internship.ForeignKeys[0]: if value, ok := values[j].(*sql.NullString); !ok { @@ -341,11 +361,21 @@ func (i *Internship) String() string { builder.WriteString("price=") builder.WriteString(fmt.Sprintf("%v", i.Price)) builder.WriteString(", ") - builder.WriteString("flat_discount=") - builder.WriteString(fmt.Sprintf("%v", i.FlatDiscount)) + if v := i.FlatDiscount; v != nil { + builder.WriteString("flat_discount=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + if v := i.PercentageDiscount; v != nil { + builder.WriteString("percentage_discount=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("subtotal=") + builder.WriteString(fmt.Sprintf("%v", i.Subtotal)) builder.WriteString(", ") - builder.WriteString("percentage_discount=") - builder.WriteString(fmt.Sprintf("%v", i.PercentageDiscount)) + builder.WriteString("total=") + builder.WriteString(fmt.Sprintf("%v", i.Total)) builder.WriteByte(')') return builder.String() } diff --git a/ent/internship/internship.go b/ent/internship/internship.go index e61b722..aca2bf4 100644 --- a/ent/internship/internship.go +++ b/ent/internship/internship.go @@ -7,6 +7,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/shopspring/decimal" ) const ( @@ -52,6 +53,10 @@ const ( FieldFlatDiscount = "flat_discount" // FieldPercentageDiscount holds the string denoting the percentage_discount field in the database. FieldPercentageDiscount = "percentage_discount" + // FieldSubtotal holds the string denoting the subtotal field in the database. + FieldSubtotal = "subtotal" + // FieldTotal holds the string denoting the total field in the database. + FieldTotal = "total" // EdgeCategories holds the string denoting the categories edge name in mutations. EdgeCategories = "categories" // Table holds the table name of the internship in the database. @@ -87,6 +92,8 @@ var Columns = []string{ FieldPrice, FieldFlatDiscount, FieldPercentageDiscount, + FieldSubtotal, + FieldTotal, } // ForeignKeys holds the SQL foreign-keys that are owned by the "internships" @@ -129,6 +136,10 @@ var ( DefaultSkills []string // ModeValidator is a validator for the "mode" field. It is called by the builders before save. ModeValidator func(string) error + // DefaultSubtotal holds the default value on creation for the "subtotal" field. + DefaultSubtotal decimal.Decimal + // DefaultTotal holds the default value on creation for the "total" field. + DefaultTotal decimal.Decimal // DefaultID holds the default value on creation for the "id" field. DefaultID func() string ) @@ -216,6 +227,16 @@ func ByPercentageDiscount(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldPercentageDiscount, opts...).ToFunc() } +// BySubtotal orders the results by the subtotal field. +func BySubtotal(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubtotal, opts...).ToFunc() +} + +// ByTotal orders the results by the total field. +func ByTotal(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTotal, opts...).ToFunc() +} + // ByCategoriesCount orders the results by categories count. func ByCategoriesCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/ent/internship/where.go b/ent/internship/where.go index a558b9b..85199f0 100644 --- a/ent/internship/where.go +++ b/ent/internship/where.go @@ -141,6 +141,16 @@ func PercentageDiscount(v decimal.Decimal) predicate.Internship { return predicate.Internship(sql.FieldEQ(FieldPercentageDiscount, v)) } +// Subtotal applies equality check predicate on the "subtotal" field. It's identical to SubtotalEQ. +func Subtotal(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldEQ(FieldSubtotal, v)) +} + +// Total applies equality check predicate on the "total" field. It's identical to TotalEQ. +func Total(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldEQ(FieldTotal, v)) +} + // StatusEQ applies the EQ predicate on the "status" field. func StatusEQ(v string) predicate.Internship { return predicate.Internship(sql.FieldEQ(FieldStatus, v)) @@ -976,16 +986,6 @@ func PriceLTE(v decimal.Decimal) predicate.Internship { return predicate.Internship(sql.FieldLTE(FieldPrice, v)) } -// PriceIsNil applies the IsNil predicate on the "price" field. -func PriceIsNil() predicate.Internship { - return predicate.Internship(sql.FieldIsNull(FieldPrice)) -} - -// PriceNotNil applies the NotNil predicate on the "price" field. -func PriceNotNil() predicate.Internship { - return predicate.Internship(sql.FieldNotNull(FieldPrice)) -} - // FlatDiscountEQ applies the EQ predicate on the "flat_discount" field. func FlatDiscountEQ(v decimal.Decimal) predicate.Internship { return predicate.Internship(sql.FieldEQ(FieldFlatDiscount, v)) @@ -1086,6 +1086,86 @@ func PercentageDiscountNotNil() predicate.Internship { return predicate.Internship(sql.FieldNotNull(FieldPercentageDiscount)) } +// SubtotalEQ applies the EQ predicate on the "subtotal" field. +func SubtotalEQ(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldEQ(FieldSubtotal, v)) +} + +// SubtotalNEQ applies the NEQ predicate on the "subtotal" field. +func SubtotalNEQ(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldNEQ(FieldSubtotal, v)) +} + +// SubtotalIn applies the In predicate on the "subtotal" field. +func SubtotalIn(vs ...decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldIn(FieldSubtotal, vs...)) +} + +// SubtotalNotIn applies the NotIn predicate on the "subtotal" field. +func SubtotalNotIn(vs ...decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldNotIn(FieldSubtotal, vs...)) +} + +// SubtotalGT applies the GT predicate on the "subtotal" field. +func SubtotalGT(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldGT(FieldSubtotal, v)) +} + +// SubtotalGTE applies the GTE predicate on the "subtotal" field. +func SubtotalGTE(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldGTE(FieldSubtotal, v)) +} + +// SubtotalLT applies the LT predicate on the "subtotal" field. +func SubtotalLT(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldLT(FieldSubtotal, v)) +} + +// SubtotalLTE applies the LTE predicate on the "subtotal" field. +func SubtotalLTE(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldLTE(FieldSubtotal, v)) +} + +// TotalEQ applies the EQ predicate on the "total" field. +func TotalEQ(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldEQ(FieldTotal, v)) +} + +// TotalNEQ applies the NEQ predicate on the "total" field. +func TotalNEQ(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldNEQ(FieldTotal, v)) +} + +// TotalIn applies the In predicate on the "total" field. +func TotalIn(vs ...decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldIn(FieldTotal, vs...)) +} + +// TotalNotIn applies the NotIn predicate on the "total" field. +func TotalNotIn(vs ...decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldNotIn(FieldTotal, vs...)) +} + +// TotalGT applies the GT predicate on the "total" field. +func TotalGT(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldGT(FieldTotal, v)) +} + +// TotalGTE applies the GTE predicate on the "total" field. +func TotalGTE(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldGTE(FieldTotal, v)) +} + +// TotalLT applies the LT predicate on the "total" field. +func TotalLT(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldLT(FieldTotal, v)) +} + +// TotalLTE applies the LTE predicate on the "total" field. +func TotalLTE(v decimal.Decimal) predicate.Internship { + return predicate.Internship(sql.FieldLTE(FieldTotal, v)) +} + // HasCategories applies the HasEdge predicate on the "categories" edge. func HasCategories() predicate.Internship { return predicate.Internship(func(s *sql.Selector) { diff --git a/ent/internship_create.go b/ent/internship_create.go index dcaa809..90e0097 100644 --- a/ent/internship_create.go +++ b/ent/internship_create.go @@ -188,14 +188,6 @@ func (ic *InternshipCreate) SetPrice(d decimal.Decimal) *InternshipCreate { return ic } -// SetNillablePrice sets the "price" field if the given value is not nil. -func (ic *InternshipCreate) SetNillablePrice(d *decimal.Decimal) *InternshipCreate { - if d != nil { - ic.SetPrice(*d) - } - return ic -} - // SetFlatDiscount sets the "flat_discount" field. func (ic *InternshipCreate) SetFlatDiscount(d decimal.Decimal) *InternshipCreate { ic.mutation.SetFlatDiscount(d) @@ -224,6 +216,34 @@ func (ic *InternshipCreate) SetNillablePercentageDiscount(d *decimal.Decimal) *I return ic } +// SetSubtotal sets the "subtotal" field. +func (ic *InternshipCreate) SetSubtotal(d decimal.Decimal) *InternshipCreate { + ic.mutation.SetSubtotal(d) + return ic +} + +// SetNillableSubtotal sets the "subtotal" field if the given value is not nil. +func (ic *InternshipCreate) SetNillableSubtotal(d *decimal.Decimal) *InternshipCreate { + if d != nil { + ic.SetSubtotal(*d) + } + return ic +} + +// SetTotal sets the "total" field. +func (ic *InternshipCreate) SetTotal(d decimal.Decimal) *InternshipCreate { + ic.mutation.SetTotal(d) + return ic +} + +// SetNillableTotal sets the "total" field if the given value is not nil. +func (ic *InternshipCreate) SetNillableTotal(d *decimal.Decimal) *InternshipCreate { + if d != nil { + ic.SetTotal(*d) + } + return ic +} + // SetID sets the "id" field. func (ic *InternshipCreate) SetID(s string) *InternshipCreate { ic.mutation.SetID(s) @@ -304,6 +324,14 @@ func (ic *InternshipCreate) defaults() { v := internship.DefaultSkills ic.mutation.SetSkills(v) } + if _, ok := ic.mutation.Subtotal(); !ok { + v := internship.DefaultSubtotal + ic.mutation.SetSubtotal(v) + } + if _, ok := ic.mutation.Total(); !ok { + v := internship.DefaultTotal + ic.mutation.SetTotal(v) + } if _, ok := ic.mutation.ID(); !ok { v := internship.DefaultID() ic.mutation.SetID(v) @@ -353,6 +381,15 @@ func (ic *InternshipCreate) check() error { return &ValidationError{Name: "mode", err: fmt.Errorf(`ent: validator failed for field "Internship.mode": %w`, err)} } } + if _, ok := ic.mutation.Price(); !ok { + return &ValidationError{Name: "price", err: errors.New(`ent: missing required field "Internship.price"`)} + } + if _, ok := ic.mutation.Subtotal(); !ok { + return &ValidationError{Name: "subtotal", err: errors.New(`ent: missing required field "Internship.subtotal"`)} + } + if _, ok := ic.mutation.Total(); !ok { + return &ValidationError{Name: "total", err: errors.New(`ent: missing required field "Internship.total"`)} + } return nil } @@ -458,11 +495,19 @@ func (ic *InternshipCreate) createSpec() (*Internship, *sqlgraph.CreateSpec) { } if value, ok := ic.mutation.FlatDiscount(); ok { _spec.SetField(internship.FieldFlatDiscount, field.TypeOther, value) - _node.FlatDiscount = value + _node.FlatDiscount = &value } if value, ok := ic.mutation.PercentageDiscount(); ok { _spec.SetField(internship.FieldPercentageDiscount, field.TypeOther, value) - _node.PercentageDiscount = value + _node.PercentageDiscount = &value + } + if value, ok := ic.mutation.Subtotal(); ok { + _spec.SetField(internship.FieldSubtotal, field.TypeOther, value) + _node.Subtotal = value + } + if value, ok := ic.mutation.Total(); ok { + _spec.SetField(internship.FieldTotal, field.TypeOther, value) + _node.Total = value } if nodes := ic.mutation.CategoriesIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ diff --git a/ent/internship_update.go b/ent/internship_update.go index 1626f62..fb2f8f7 100644 --- a/ent/internship_update.go +++ b/ent/internship_update.go @@ -280,12 +280,6 @@ func (iu *InternshipUpdate) SetNillablePrice(d *decimal.Decimal) *InternshipUpda return iu } -// ClearPrice clears the value of the "price" field. -func (iu *InternshipUpdate) ClearPrice() *InternshipUpdate { - iu.mutation.ClearPrice() - return iu -} - // SetFlatDiscount sets the "flat_discount" field. func (iu *InternshipUpdate) SetFlatDiscount(d decimal.Decimal) *InternshipUpdate { iu.mutation.SetFlatDiscount(d) @@ -326,6 +320,34 @@ func (iu *InternshipUpdate) ClearPercentageDiscount() *InternshipUpdate { return iu } +// SetSubtotal sets the "subtotal" field. +func (iu *InternshipUpdate) SetSubtotal(d decimal.Decimal) *InternshipUpdate { + iu.mutation.SetSubtotal(d) + return iu +} + +// SetNillableSubtotal sets the "subtotal" field if the given value is not nil. +func (iu *InternshipUpdate) SetNillableSubtotal(d *decimal.Decimal) *InternshipUpdate { + if d != nil { + iu.SetSubtotal(*d) + } + return iu +} + +// SetTotal sets the "total" field. +func (iu *InternshipUpdate) SetTotal(d decimal.Decimal) *InternshipUpdate { + iu.mutation.SetTotal(d) + return iu +} + +// SetNillableTotal sets the "total" field if the given value is not nil. +func (iu *InternshipUpdate) SetNillableTotal(d *decimal.Decimal) *InternshipUpdate { + if d != nil { + iu.SetTotal(*d) + } + return iu +} + // AddCategoryIDs adds the "categories" edge to the Category entity by IDs. func (iu *InternshipUpdate) AddCategoryIDs(ids ...string) *InternshipUpdate { iu.mutation.AddCategoryIDs(ids...) @@ -535,9 +557,6 @@ func (iu *InternshipUpdate) sqlSave(ctx context.Context) (n int, err error) { if value, ok := iu.mutation.Price(); ok { _spec.SetField(internship.FieldPrice, field.TypeOther, value) } - if iu.mutation.PriceCleared() { - _spec.ClearField(internship.FieldPrice, field.TypeOther) - } if value, ok := iu.mutation.FlatDiscount(); ok { _spec.SetField(internship.FieldFlatDiscount, field.TypeOther, value) } @@ -550,6 +569,12 @@ func (iu *InternshipUpdate) sqlSave(ctx context.Context) (n int, err error) { if iu.mutation.PercentageDiscountCleared() { _spec.ClearField(internship.FieldPercentageDiscount, field.TypeOther) } + if value, ok := iu.mutation.Subtotal(); ok { + _spec.SetField(internship.FieldSubtotal, field.TypeOther, value) + } + if value, ok := iu.mutation.Total(); ok { + _spec.SetField(internship.FieldTotal, field.TypeOther, value) + } if iu.mutation.CategoriesCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -864,12 +889,6 @@ func (iuo *InternshipUpdateOne) SetNillablePrice(d *decimal.Decimal) *Internship return iuo } -// ClearPrice clears the value of the "price" field. -func (iuo *InternshipUpdateOne) ClearPrice() *InternshipUpdateOne { - iuo.mutation.ClearPrice() - return iuo -} - // SetFlatDiscount sets the "flat_discount" field. func (iuo *InternshipUpdateOne) SetFlatDiscount(d decimal.Decimal) *InternshipUpdateOne { iuo.mutation.SetFlatDiscount(d) @@ -910,6 +929,34 @@ func (iuo *InternshipUpdateOne) ClearPercentageDiscount() *InternshipUpdateOne { return iuo } +// SetSubtotal sets the "subtotal" field. +func (iuo *InternshipUpdateOne) SetSubtotal(d decimal.Decimal) *InternshipUpdateOne { + iuo.mutation.SetSubtotal(d) + return iuo +} + +// SetNillableSubtotal sets the "subtotal" field if the given value is not nil. +func (iuo *InternshipUpdateOne) SetNillableSubtotal(d *decimal.Decimal) *InternshipUpdateOne { + if d != nil { + iuo.SetSubtotal(*d) + } + return iuo +} + +// SetTotal sets the "total" field. +func (iuo *InternshipUpdateOne) SetTotal(d decimal.Decimal) *InternshipUpdateOne { + iuo.mutation.SetTotal(d) + return iuo +} + +// SetNillableTotal sets the "total" field if the given value is not nil. +func (iuo *InternshipUpdateOne) SetNillableTotal(d *decimal.Decimal) *InternshipUpdateOne { + if d != nil { + iuo.SetTotal(*d) + } + return iuo +} + // AddCategoryIDs adds the "categories" edge to the Category entity by IDs. func (iuo *InternshipUpdateOne) AddCategoryIDs(ids ...string) *InternshipUpdateOne { iuo.mutation.AddCategoryIDs(ids...) @@ -1149,9 +1196,6 @@ func (iuo *InternshipUpdateOne) sqlSave(ctx context.Context) (_node *Internship, if value, ok := iuo.mutation.Price(); ok { _spec.SetField(internship.FieldPrice, field.TypeOther, value) } - if iuo.mutation.PriceCleared() { - _spec.ClearField(internship.FieldPrice, field.TypeOther) - } if value, ok := iuo.mutation.FlatDiscount(); ok { _spec.SetField(internship.FieldFlatDiscount, field.TypeOther, value) } @@ -1164,6 +1208,12 @@ func (iuo *InternshipUpdateOne) sqlSave(ctx context.Context) (_node *Internship, if iuo.mutation.PercentageDiscountCleared() { _spec.ClearField(internship.FieldPercentageDiscount, field.TypeOther) } + if value, ok := iuo.mutation.Subtotal(); ok { + _spec.SetField(internship.FieldSubtotal, field.TypeOther, value) + } + if value, ok := iuo.mutation.Total(); ok { + _spec.SetField(internship.FieldTotal, field.TypeOther, value) + } if iuo.mutation.CategoriesCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/ent/internshipbatch.go b/ent/internshipbatch.go new file mode 100644 index 0000000..190dfc7 --- /dev/null +++ b/ent/internshipbatch.go @@ -0,0 +1,230 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/internshipbatch" +) + +// InternshipBatch is the model entity for the InternshipBatch schema. +type InternshipBatch struct { + config `json:"-"` + // ID of the ent. + ID string `json:"id,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // UpdatedBy holds the value of the "updated_by" field. + UpdatedBy string `json:"updated_by,omitempty"` + // Metadata holds the value of the "metadata" field. + Metadata map[string]string `json:"metadata,omitempty"` + // InternshipID holds the value of the "internship_id" field. + InternshipID string `json:"internship_id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // StartDate holds the value of the "start_date" field. + StartDate time.Time `json:"start_date,omitempty"` + // EndDate holds the value of the "end_date" field. + EndDate time.Time `json:"end_date,omitempty"` + // BatchStatus holds the value of the "batch_status" field. + BatchStatus string `json:"batch_status,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*InternshipBatch) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case internshipbatch.FieldMetadata: + values[i] = new([]byte) + case internshipbatch.FieldID, internshipbatch.FieldStatus, internshipbatch.FieldCreatedBy, internshipbatch.FieldUpdatedBy, internshipbatch.FieldInternshipID, internshipbatch.FieldName, internshipbatch.FieldDescription, internshipbatch.FieldBatchStatus: + values[i] = new(sql.NullString) + case internshipbatch.FieldCreatedAt, internshipbatch.FieldUpdatedAt, internshipbatch.FieldStartDate, internshipbatch.FieldEndDate: + values[i] = new(sql.NullTime) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the InternshipBatch fields. +func (ib *InternshipBatch) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case internshipbatch.FieldID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value.Valid { + ib.ID = value.String + } + case internshipbatch.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + ib.Status = value.String + } + case internshipbatch.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + ib.CreatedAt = value.Time + } + case internshipbatch.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + ib.UpdatedAt = value.Time + } + case internshipbatch.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + ib.CreatedBy = value.String + } + case internshipbatch.FieldUpdatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field updated_by", values[i]) + } else if value.Valid { + ib.UpdatedBy = value.String + } + case internshipbatch.FieldMetadata: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field metadata", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &ib.Metadata); err != nil { + return fmt.Errorf("unmarshal field metadata: %w", err) + } + } + case internshipbatch.FieldInternshipID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field internship_id", values[i]) + } else if value.Valid { + ib.InternshipID = value.String + } + case internshipbatch.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + ib.Name = value.String + } + case internshipbatch.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + ib.Description = value.String + } + case internshipbatch.FieldStartDate: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field start_date", values[i]) + } else if value.Valid { + ib.StartDate = value.Time + } + case internshipbatch.FieldEndDate: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field end_date", values[i]) + } else if value.Valid { + ib.EndDate = value.Time + } + case internshipbatch.FieldBatchStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field batch_status", values[i]) + } else if value.Valid { + ib.BatchStatus = value.String + } + default: + ib.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the InternshipBatch. +// This includes values selected through modifiers, order, etc. +func (ib *InternshipBatch) Value(name string) (ent.Value, error) { + return ib.selectValues.Get(name) +} + +// Update returns a builder for updating this InternshipBatch. +// Note that you need to call InternshipBatch.Unwrap() before calling this method if this InternshipBatch +// was returned from a transaction, and the transaction was committed or rolled back. +func (ib *InternshipBatch) Update() *InternshipBatchUpdateOne { + return NewInternshipBatchClient(ib.config).UpdateOne(ib) +} + +// Unwrap unwraps the InternshipBatch entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (ib *InternshipBatch) Unwrap() *InternshipBatch { + _tx, ok := ib.config.driver.(*txDriver) + if !ok { + panic("ent: InternshipBatch is not a transactional entity") + } + ib.config.driver = _tx.drv + return ib +} + +// String implements the fmt.Stringer. +func (ib *InternshipBatch) String() string { + var builder strings.Builder + builder.WriteString("InternshipBatch(") + builder.WriteString(fmt.Sprintf("id=%v, ", ib.ID)) + builder.WriteString("status=") + builder.WriteString(ib.Status) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(ib.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(ib.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(ib.CreatedBy) + builder.WriteString(", ") + builder.WriteString("updated_by=") + builder.WriteString(ib.UpdatedBy) + builder.WriteString(", ") + builder.WriteString("metadata=") + builder.WriteString(fmt.Sprintf("%v", ib.Metadata)) + builder.WriteString(", ") + builder.WriteString("internship_id=") + builder.WriteString(ib.InternshipID) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(ib.Name) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(ib.Description) + builder.WriteString(", ") + builder.WriteString("start_date=") + builder.WriteString(ib.StartDate.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("end_date=") + builder.WriteString(ib.EndDate.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("batch_status=") + builder.WriteString(ib.BatchStatus) + builder.WriteByte(')') + return builder.String() +} + +// InternshipBatches is a parsable slice of InternshipBatch. +type InternshipBatches []*InternshipBatch diff --git a/ent/internshipbatch/internshipbatch.go b/ent/internshipbatch/internshipbatch.go new file mode 100644 index 0000000..a5ee262 --- /dev/null +++ b/ent/internshipbatch/internshipbatch.go @@ -0,0 +1,155 @@ +// Code generated by ent, DO NOT EDIT. + +package internshipbatch + +import ( + "time" + + "entgo.io/ent/dialect/sql" +) + +const ( + // Label holds the string label denoting the internshipbatch type in the database. + Label = "internship_batch" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldUpdatedBy holds the string denoting the updated_by field in the database. + FieldUpdatedBy = "updated_by" + // FieldMetadata holds the string denoting the metadata field in the database. + FieldMetadata = "metadata" + // FieldInternshipID holds the string denoting the internship_id field in the database. + FieldInternshipID = "internship_id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldStartDate holds the string denoting the start_date field in the database. + FieldStartDate = "start_date" + // FieldEndDate holds the string denoting the end_date field in the database. + FieldEndDate = "end_date" + // FieldBatchStatus holds the string denoting the batch_status field in the database. + FieldBatchStatus = "batch_status" + // Table holds the table name of the internshipbatch in the database. + Table = "internship_batches" +) + +// Columns holds all SQL columns for internshipbatch fields. +var Columns = []string{ + FieldID, + FieldStatus, + FieldCreatedAt, + FieldUpdatedAt, + FieldCreatedBy, + FieldUpdatedBy, + FieldMetadata, + FieldInternshipID, + FieldName, + FieldDescription, + FieldStartDate, + FieldEndDate, + FieldBatchStatus, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultMetadata holds the default value on creation for the "metadata" field. + DefaultMetadata map[string]string + // InternshipIDValidator is a validator for the "internship_id" field. It is called by the builders before save. + InternshipIDValidator func(string) error + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // DefaultBatchStatus holds the default value on creation for the "batch_status" field. + DefaultBatchStatus string + // BatchStatusValidator is a validator for the "batch_status" field. It is called by the builders before save. + BatchStatusValidator func(string) error + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() string +) + +// OrderOption defines the ordering options for the InternshipBatch queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByUpdatedBy orders the results by the updated_by field. +func ByUpdatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedBy, opts...).ToFunc() +} + +// ByInternshipID orders the results by the internship_id field. +func ByInternshipID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInternshipID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// ByStartDate orders the results by the start_date field. +func ByStartDate(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStartDate, opts...).ToFunc() +} + +// ByEndDate orders the results by the end_date field. +func ByEndDate(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEndDate, opts...).ToFunc() +} + +// ByBatchStatus orders the results by the batch_status field. +func ByBatchStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBatchStatus, opts...).ToFunc() +} diff --git a/ent/internshipbatch/where.go b/ent/internshipbatch/where.go new file mode 100644 index 0000000..897f1c6 --- /dev/null +++ b/ent/internshipbatch/where.go @@ -0,0 +1,810 @@ +// Code generated by ent, DO NOT EDIT. + +package internshipbatch + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldID, id)) +} + +// IDEqualFold applies the EqualFold predicate on the ID field. +func IDEqualFold(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEqualFold(FieldID, id)) +} + +// IDContainsFold applies the ContainsFold predicate on the ID field. +func IDContainsFold(id string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContainsFold(FieldID, id)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldStatus, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldCreatedBy, v)) +} + +// UpdatedBy applies equality check predicate on the "updated_by" field. It's identical to UpdatedByEQ. +func UpdatedBy(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// InternshipID applies equality check predicate on the "internship_id" field. It's identical to InternshipIDEQ. +func InternshipID(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldInternshipID, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldName, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldDescription, v)) +} + +// StartDate applies equality check predicate on the "start_date" field. It's identical to StartDateEQ. +func StartDate(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldStartDate, v)) +} + +// EndDate applies equality check predicate on the "end_date" field. It's identical to EndDateEQ. +func EndDate(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldEndDate, v)) +} + +// BatchStatus applies equality check predicate on the "batch_status" field. It's identical to BatchStatusEQ. +func BatchStatus(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldBatchStatus, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContainsFold(FieldStatus, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// UpdatedByEQ applies the EQ predicate on the "updated_by" field. +func UpdatedByEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UpdatedByNEQ applies the NEQ predicate on the "updated_by" field. +func UpdatedByNEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldUpdatedBy, v)) +} + +// UpdatedByIn applies the In predicate on the "updated_by" field. +func UpdatedByIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByNotIn applies the NotIn predicate on the "updated_by" field. +func UpdatedByNotIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByGT applies the GT predicate on the "updated_by" field. +func UpdatedByGT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldUpdatedBy, v)) +} + +// UpdatedByGTE applies the GTE predicate on the "updated_by" field. +func UpdatedByGTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldUpdatedBy, v)) +} + +// UpdatedByLT applies the LT predicate on the "updated_by" field. +func UpdatedByLT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldUpdatedBy, v)) +} + +// UpdatedByLTE applies the LTE predicate on the "updated_by" field. +func UpdatedByLTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldUpdatedBy, v)) +} + +// UpdatedByContains applies the Contains predicate on the "updated_by" field. +func UpdatedByContains(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContains(FieldUpdatedBy, v)) +} + +// UpdatedByHasPrefix applies the HasPrefix predicate on the "updated_by" field. +func UpdatedByHasPrefix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasPrefix(FieldUpdatedBy, v)) +} + +// UpdatedByHasSuffix applies the HasSuffix predicate on the "updated_by" field. +func UpdatedByHasSuffix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasSuffix(FieldUpdatedBy, v)) +} + +// UpdatedByIsNil applies the IsNil predicate on the "updated_by" field. +func UpdatedByIsNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIsNull(FieldUpdatedBy)) +} + +// UpdatedByNotNil applies the NotNil predicate on the "updated_by" field. +func UpdatedByNotNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotNull(FieldUpdatedBy)) +} + +// UpdatedByEqualFold applies the EqualFold predicate on the "updated_by" field. +func UpdatedByEqualFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEqualFold(FieldUpdatedBy, v)) +} + +// UpdatedByContainsFold applies the ContainsFold predicate on the "updated_by" field. +func UpdatedByContainsFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContainsFold(FieldUpdatedBy, v)) +} + +// MetadataIsNil applies the IsNil predicate on the "metadata" field. +func MetadataIsNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIsNull(FieldMetadata)) +} + +// MetadataNotNil applies the NotNil predicate on the "metadata" field. +func MetadataNotNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotNull(FieldMetadata)) +} + +// InternshipIDEQ applies the EQ predicate on the "internship_id" field. +func InternshipIDEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldInternshipID, v)) +} + +// InternshipIDNEQ applies the NEQ predicate on the "internship_id" field. +func InternshipIDNEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldInternshipID, v)) +} + +// InternshipIDIn applies the In predicate on the "internship_id" field. +func InternshipIDIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldInternshipID, vs...)) +} + +// InternshipIDNotIn applies the NotIn predicate on the "internship_id" field. +func InternshipIDNotIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldInternshipID, vs...)) +} + +// InternshipIDGT applies the GT predicate on the "internship_id" field. +func InternshipIDGT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldInternshipID, v)) +} + +// InternshipIDGTE applies the GTE predicate on the "internship_id" field. +func InternshipIDGTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldInternshipID, v)) +} + +// InternshipIDLT applies the LT predicate on the "internship_id" field. +func InternshipIDLT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldInternshipID, v)) +} + +// InternshipIDLTE applies the LTE predicate on the "internship_id" field. +func InternshipIDLTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldInternshipID, v)) +} + +// InternshipIDContains applies the Contains predicate on the "internship_id" field. +func InternshipIDContains(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContains(FieldInternshipID, v)) +} + +// InternshipIDHasPrefix applies the HasPrefix predicate on the "internship_id" field. +func InternshipIDHasPrefix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasPrefix(FieldInternshipID, v)) +} + +// InternshipIDHasSuffix applies the HasSuffix predicate on the "internship_id" field. +func InternshipIDHasSuffix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasSuffix(FieldInternshipID, v)) +} + +// InternshipIDEqualFold applies the EqualFold predicate on the "internship_id" field. +func InternshipIDEqualFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEqualFold(FieldInternshipID, v)) +} + +// InternshipIDContainsFold applies the ContainsFold predicate on the "internship_id" field. +func InternshipIDContainsFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContainsFold(FieldInternshipID, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContainsFold(FieldName, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionIsNil applies the IsNil predicate on the "description" field. +func DescriptionIsNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIsNull(FieldDescription)) +} + +// DescriptionNotNil applies the NotNil predicate on the "description" field. +func DescriptionNotNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotNull(FieldDescription)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContainsFold(FieldDescription, v)) +} + +// StartDateEQ applies the EQ predicate on the "start_date" field. +func StartDateEQ(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldStartDate, v)) +} + +// StartDateNEQ applies the NEQ predicate on the "start_date" field. +func StartDateNEQ(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldStartDate, v)) +} + +// StartDateIn applies the In predicate on the "start_date" field. +func StartDateIn(vs ...time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldStartDate, vs...)) +} + +// StartDateNotIn applies the NotIn predicate on the "start_date" field. +func StartDateNotIn(vs ...time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldStartDate, vs...)) +} + +// StartDateGT applies the GT predicate on the "start_date" field. +func StartDateGT(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldStartDate, v)) +} + +// StartDateGTE applies the GTE predicate on the "start_date" field. +func StartDateGTE(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldStartDate, v)) +} + +// StartDateLT applies the LT predicate on the "start_date" field. +func StartDateLT(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldStartDate, v)) +} + +// StartDateLTE applies the LTE predicate on the "start_date" field. +func StartDateLTE(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldStartDate, v)) +} + +// StartDateIsNil applies the IsNil predicate on the "start_date" field. +func StartDateIsNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIsNull(FieldStartDate)) +} + +// StartDateNotNil applies the NotNil predicate on the "start_date" field. +func StartDateNotNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotNull(FieldStartDate)) +} + +// EndDateEQ applies the EQ predicate on the "end_date" field. +func EndDateEQ(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldEndDate, v)) +} + +// EndDateNEQ applies the NEQ predicate on the "end_date" field. +func EndDateNEQ(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldEndDate, v)) +} + +// EndDateIn applies the In predicate on the "end_date" field. +func EndDateIn(vs ...time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldEndDate, vs...)) +} + +// EndDateNotIn applies the NotIn predicate on the "end_date" field. +func EndDateNotIn(vs ...time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldEndDate, vs...)) +} + +// EndDateGT applies the GT predicate on the "end_date" field. +func EndDateGT(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldEndDate, v)) +} + +// EndDateGTE applies the GTE predicate on the "end_date" field. +func EndDateGTE(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldEndDate, v)) +} + +// EndDateLT applies the LT predicate on the "end_date" field. +func EndDateLT(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldEndDate, v)) +} + +// EndDateLTE applies the LTE predicate on the "end_date" field. +func EndDateLTE(v time.Time) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldEndDate, v)) +} + +// EndDateIsNil applies the IsNil predicate on the "end_date" field. +func EndDateIsNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIsNull(FieldEndDate)) +} + +// EndDateNotNil applies the NotNil predicate on the "end_date" field. +func EndDateNotNil() predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotNull(FieldEndDate)) +} + +// BatchStatusEQ applies the EQ predicate on the "batch_status" field. +func BatchStatusEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEQ(FieldBatchStatus, v)) +} + +// BatchStatusNEQ applies the NEQ predicate on the "batch_status" field. +func BatchStatusNEQ(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNEQ(FieldBatchStatus, v)) +} + +// BatchStatusIn applies the In predicate on the "batch_status" field. +func BatchStatusIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldIn(FieldBatchStatus, vs...)) +} + +// BatchStatusNotIn applies the NotIn predicate on the "batch_status" field. +func BatchStatusNotIn(vs ...string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldNotIn(FieldBatchStatus, vs...)) +} + +// BatchStatusGT applies the GT predicate on the "batch_status" field. +func BatchStatusGT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGT(FieldBatchStatus, v)) +} + +// BatchStatusGTE applies the GTE predicate on the "batch_status" field. +func BatchStatusGTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldGTE(FieldBatchStatus, v)) +} + +// BatchStatusLT applies the LT predicate on the "batch_status" field. +func BatchStatusLT(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLT(FieldBatchStatus, v)) +} + +// BatchStatusLTE applies the LTE predicate on the "batch_status" field. +func BatchStatusLTE(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldLTE(FieldBatchStatus, v)) +} + +// BatchStatusContains applies the Contains predicate on the "batch_status" field. +func BatchStatusContains(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContains(FieldBatchStatus, v)) +} + +// BatchStatusHasPrefix applies the HasPrefix predicate on the "batch_status" field. +func BatchStatusHasPrefix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasPrefix(FieldBatchStatus, v)) +} + +// BatchStatusHasSuffix applies the HasSuffix predicate on the "batch_status" field. +func BatchStatusHasSuffix(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldHasSuffix(FieldBatchStatus, v)) +} + +// BatchStatusEqualFold applies the EqualFold predicate on the "batch_status" field. +func BatchStatusEqualFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldEqualFold(FieldBatchStatus, v)) +} + +// BatchStatusContainsFold applies the ContainsFold predicate on the "batch_status" field. +func BatchStatusContainsFold(v string) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.FieldContainsFold(FieldBatchStatus, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.InternshipBatch) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.InternshipBatch) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.InternshipBatch) predicate.InternshipBatch { + return predicate.InternshipBatch(sql.NotPredicates(p)) +} diff --git a/ent/internshipbatch_create.go b/ent/internshipbatch_create.go new file mode 100644 index 0000000..de1cf23 --- /dev/null +++ b/ent/internshipbatch_create.go @@ -0,0 +1,445 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/internshipbatch" +) + +// InternshipBatchCreate is the builder for creating a InternshipBatch entity. +type InternshipBatchCreate struct { + config + mutation *InternshipBatchMutation + hooks []Hook +} + +// SetStatus sets the "status" field. +func (ibc *InternshipBatchCreate) SetStatus(s string) *InternshipBatchCreate { + ibc.mutation.SetStatus(s) + return ibc +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableStatus(s *string) *InternshipBatchCreate { + if s != nil { + ibc.SetStatus(*s) + } + return ibc +} + +// SetCreatedAt sets the "created_at" field. +func (ibc *InternshipBatchCreate) SetCreatedAt(t time.Time) *InternshipBatchCreate { + ibc.mutation.SetCreatedAt(t) + return ibc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableCreatedAt(t *time.Time) *InternshipBatchCreate { + if t != nil { + ibc.SetCreatedAt(*t) + } + return ibc +} + +// SetUpdatedAt sets the "updated_at" field. +func (ibc *InternshipBatchCreate) SetUpdatedAt(t time.Time) *InternshipBatchCreate { + ibc.mutation.SetUpdatedAt(t) + return ibc +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableUpdatedAt(t *time.Time) *InternshipBatchCreate { + if t != nil { + ibc.SetUpdatedAt(*t) + } + return ibc +} + +// SetCreatedBy sets the "created_by" field. +func (ibc *InternshipBatchCreate) SetCreatedBy(s string) *InternshipBatchCreate { + ibc.mutation.SetCreatedBy(s) + return ibc +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableCreatedBy(s *string) *InternshipBatchCreate { + if s != nil { + ibc.SetCreatedBy(*s) + } + return ibc +} + +// SetUpdatedBy sets the "updated_by" field. +func (ibc *InternshipBatchCreate) SetUpdatedBy(s string) *InternshipBatchCreate { + ibc.mutation.SetUpdatedBy(s) + return ibc +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableUpdatedBy(s *string) *InternshipBatchCreate { + if s != nil { + ibc.SetUpdatedBy(*s) + } + return ibc +} + +// SetMetadata sets the "metadata" field. +func (ibc *InternshipBatchCreate) SetMetadata(m map[string]string) *InternshipBatchCreate { + ibc.mutation.SetMetadata(m) + return ibc +} + +// SetInternshipID sets the "internship_id" field. +func (ibc *InternshipBatchCreate) SetInternshipID(s string) *InternshipBatchCreate { + ibc.mutation.SetInternshipID(s) + return ibc +} + +// SetName sets the "name" field. +func (ibc *InternshipBatchCreate) SetName(s string) *InternshipBatchCreate { + ibc.mutation.SetName(s) + return ibc +} + +// SetDescription sets the "description" field. +func (ibc *InternshipBatchCreate) SetDescription(s string) *InternshipBatchCreate { + ibc.mutation.SetDescription(s) + return ibc +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableDescription(s *string) *InternshipBatchCreate { + if s != nil { + ibc.SetDescription(*s) + } + return ibc +} + +// SetStartDate sets the "start_date" field. +func (ibc *InternshipBatchCreate) SetStartDate(t time.Time) *InternshipBatchCreate { + ibc.mutation.SetStartDate(t) + return ibc +} + +// SetNillableStartDate sets the "start_date" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableStartDate(t *time.Time) *InternshipBatchCreate { + if t != nil { + ibc.SetStartDate(*t) + } + return ibc +} + +// SetEndDate sets the "end_date" field. +func (ibc *InternshipBatchCreate) SetEndDate(t time.Time) *InternshipBatchCreate { + ibc.mutation.SetEndDate(t) + return ibc +} + +// SetNillableEndDate sets the "end_date" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableEndDate(t *time.Time) *InternshipBatchCreate { + if t != nil { + ibc.SetEndDate(*t) + } + return ibc +} + +// SetBatchStatus sets the "batch_status" field. +func (ibc *InternshipBatchCreate) SetBatchStatus(s string) *InternshipBatchCreate { + ibc.mutation.SetBatchStatus(s) + return ibc +} + +// SetNillableBatchStatus sets the "batch_status" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableBatchStatus(s *string) *InternshipBatchCreate { + if s != nil { + ibc.SetBatchStatus(*s) + } + return ibc +} + +// SetID sets the "id" field. +func (ibc *InternshipBatchCreate) SetID(s string) *InternshipBatchCreate { + ibc.mutation.SetID(s) + return ibc +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (ibc *InternshipBatchCreate) SetNillableID(s *string) *InternshipBatchCreate { + if s != nil { + ibc.SetID(*s) + } + return ibc +} + +// Mutation returns the InternshipBatchMutation object of the builder. +func (ibc *InternshipBatchCreate) Mutation() *InternshipBatchMutation { + return ibc.mutation +} + +// Save creates the InternshipBatch in the database. +func (ibc *InternshipBatchCreate) Save(ctx context.Context) (*InternshipBatch, error) { + ibc.defaults() + return withHooks(ctx, ibc.sqlSave, ibc.mutation, ibc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (ibc *InternshipBatchCreate) SaveX(ctx context.Context) *InternshipBatch { + v, err := ibc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (ibc *InternshipBatchCreate) Exec(ctx context.Context) error { + _, err := ibc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ibc *InternshipBatchCreate) ExecX(ctx context.Context) { + if err := ibc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (ibc *InternshipBatchCreate) defaults() { + if _, ok := ibc.mutation.Status(); !ok { + v := internshipbatch.DefaultStatus + ibc.mutation.SetStatus(v) + } + if _, ok := ibc.mutation.CreatedAt(); !ok { + v := internshipbatch.DefaultCreatedAt() + ibc.mutation.SetCreatedAt(v) + } + if _, ok := ibc.mutation.UpdatedAt(); !ok { + v := internshipbatch.DefaultUpdatedAt() + ibc.mutation.SetUpdatedAt(v) + } + if _, ok := ibc.mutation.Metadata(); !ok { + v := internshipbatch.DefaultMetadata + ibc.mutation.SetMetadata(v) + } + if _, ok := ibc.mutation.BatchStatus(); !ok { + v := internshipbatch.DefaultBatchStatus + ibc.mutation.SetBatchStatus(v) + } + if _, ok := ibc.mutation.ID(); !ok { + v := internshipbatch.DefaultID() + ibc.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (ibc *InternshipBatchCreate) check() error { + if _, ok := ibc.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "InternshipBatch.status"`)} + } + if _, ok := ibc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "InternshipBatch.created_at"`)} + } + if _, ok := ibc.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "InternshipBatch.updated_at"`)} + } + if _, ok := ibc.mutation.InternshipID(); !ok { + return &ValidationError{Name: "internship_id", err: errors.New(`ent: missing required field "InternshipBatch.internship_id"`)} + } + if v, ok := ibc.mutation.InternshipID(); ok { + if err := internshipbatch.InternshipIDValidator(v); err != nil { + return &ValidationError{Name: "internship_id", err: fmt.Errorf(`ent: validator failed for field "InternshipBatch.internship_id": %w`, err)} + } + } + if _, ok := ibc.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "InternshipBatch.name"`)} + } + if v, ok := ibc.mutation.Name(); ok { + if err := internshipbatch.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "InternshipBatch.name": %w`, err)} + } + } + if _, ok := ibc.mutation.BatchStatus(); !ok { + return &ValidationError{Name: "batch_status", err: errors.New(`ent: missing required field "InternshipBatch.batch_status"`)} + } + if v, ok := ibc.mutation.BatchStatus(); ok { + if err := internshipbatch.BatchStatusValidator(v); err != nil { + return &ValidationError{Name: "batch_status", err: fmt.Errorf(`ent: validator failed for field "InternshipBatch.batch_status": %w`, err)} + } + } + return nil +} + +func (ibc *InternshipBatchCreate) sqlSave(ctx context.Context) (*InternshipBatch, error) { + if err := ibc.check(); err != nil { + return nil, err + } + _node, _spec := ibc.createSpec() + if err := sqlgraph.CreateNode(ctx, ibc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(string); ok { + _node.ID = id + } else { + return nil, fmt.Errorf("unexpected InternshipBatch.ID type: %T", _spec.ID.Value) + } + } + ibc.mutation.id = &_node.ID + ibc.mutation.done = true + return _node, nil +} + +func (ibc *InternshipBatchCreate) createSpec() (*InternshipBatch, *sqlgraph.CreateSpec) { + var ( + _node = &InternshipBatch{config: ibc.config} + _spec = sqlgraph.NewCreateSpec(internshipbatch.Table, sqlgraph.NewFieldSpec(internshipbatch.FieldID, field.TypeString)) + ) + if id, ok := ibc.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := ibc.mutation.Status(); ok { + _spec.SetField(internshipbatch.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := ibc.mutation.CreatedAt(); ok { + _spec.SetField(internshipbatch.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := ibc.mutation.UpdatedAt(); ok { + _spec.SetField(internshipbatch.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := ibc.mutation.CreatedBy(); ok { + _spec.SetField(internshipbatch.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := ibc.mutation.UpdatedBy(); ok { + _spec.SetField(internshipbatch.FieldUpdatedBy, field.TypeString, value) + _node.UpdatedBy = value + } + if value, ok := ibc.mutation.Metadata(); ok { + _spec.SetField(internshipbatch.FieldMetadata, field.TypeJSON, value) + _node.Metadata = value + } + if value, ok := ibc.mutation.InternshipID(); ok { + _spec.SetField(internshipbatch.FieldInternshipID, field.TypeString, value) + _node.InternshipID = value + } + if value, ok := ibc.mutation.Name(); ok { + _spec.SetField(internshipbatch.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := ibc.mutation.Description(); ok { + _spec.SetField(internshipbatch.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := ibc.mutation.StartDate(); ok { + _spec.SetField(internshipbatch.FieldStartDate, field.TypeTime, value) + _node.StartDate = value + } + if value, ok := ibc.mutation.EndDate(); ok { + _spec.SetField(internshipbatch.FieldEndDate, field.TypeTime, value) + _node.EndDate = value + } + if value, ok := ibc.mutation.BatchStatus(); ok { + _spec.SetField(internshipbatch.FieldBatchStatus, field.TypeString, value) + _node.BatchStatus = value + } + return _node, _spec +} + +// InternshipBatchCreateBulk is the builder for creating many InternshipBatch entities in bulk. +type InternshipBatchCreateBulk struct { + config + err error + builders []*InternshipBatchCreate +} + +// Save creates the InternshipBatch entities in the database. +func (ibcb *InternshipBatchCreateBulk) Save(ctx context.Context) ([]*InternshipBatch, error) { + if ibcb.err != nil { + return nil, ibcb.err + } + specs := make([]*sqlgraph.CreateSpec, len(ibcb.builders)) + nodes := make([]*InternshipBatch, len(ibcb.builders)) + mutators := make([]Mutator, len(ibcb.builders)) + for i := range ibcb.builders { + func(i int, root context.Context) { + builder := ibcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*InternshipBatchMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, ibcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, ibcb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, ibcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (ibcb *InternshipBatchCreateBulk) SaveX(ctx context.Context) []*InternshipBatch { + v, err := ibcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (ibcb *InternshipBatchCreateBulk) Exec(ctx context.Context) error { + _, err := ibcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ibcb *InternshipBatchCreateBulk) ExecX(ctx context.Context) { + if err := ibcb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/internshipbatch_delete.go b/ent/internshipbatch_delete.go new file mode 100644 index 0000000..08d768b --- /dev/null +++ b/ent/internshipbatch_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/internshipbatch" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// InternshipBatchDelete is the builder for deleting a InternshipBatch entity. +type InternshipBatchDelete struct { + config + hooks []Hook + mutation *InternshipBatchMutation +} + +// Where appends a list predicates to the InternshipBatchDelete builder. +func (ibd *InternshipBatchDelete) Where(ps ...predicate.InternshipBatch) *InternshipBatchDelete { + ibd.mutation.Where(ps...) + return ibd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (ibd *InternshipBatchDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, ibd.sqlExec, ibd.mutation, ibd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (ibd *InternshipBatchDelete) ExecX(ctx context.Context) int { + n, err := ibd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (ibd *InternshipBatchDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(internshipbatch.Table, sqlgraph.NewFieldSpec(internshipbatch.FieldID, field.TypeString)) + if ps := ibd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, ibd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + ibd.mutation.done = true + return affected, err +} + +// InternshipBatchDeleteOne is the builder for deleting a single InternshipBatch entity. +type InternshipBatchDeleteOne struct { + ibd *InternshipBatchDelete +} + +// Where appends a list predicates to the InternshipBatchDelete builder. +func (ibdo *InternshipBatchDeleteOne) Where(ps ...predicate.InternshipBatch) *InternshipBatchDeleteOne { + ibdo.ibd.mutation.Where(ps...) + return ibdo +} + +// Exec executes the deletion query. +func (ibdo *InternshipBatchDeleteOne) Exec(ctx context.Context) error { + n, err := ibdo.ibd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{internshipbatch.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (ibdo *InternshipBatchDeleteOne) ExecX(ctx context.Context) { + if err := ibdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/internshipbatch_query.go b/ent/internshipbatch_query.go new file mode 100644 index 0000000..283d174 --- /dev/null +++ b/ent/internshipbatch_query.go @@ -0,0 +1,527 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/internshipbatch" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// InternshipBatchQuery is the builder for querying InternshipBatch entities. +type InternshipBatchQuery struct { + config + ctx *QueryContext + order []internshipbatch.OrderOption + inters []Interceptor + predicates []predicate.InternshipBatch + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the InternshipBatchQuery builder. +func (ibq *InternshipBatchQuery) Where(ps ...predicate.InternshipBatch) *InternshipBatchQuery { + ibq.predicates = append(ibq.predicates, ps...) + return ibq +} + +// Limit the number of records to be returned by this query. +func (ibq *InternshipBatchQuery) Limit(limit int) *InternshipBatchQuery { + ibq.ctx.Limit = &limit + return ibq +} + +// Offset to start from. +func (ibq *InternshipBatchQuery) Offset(offset int) *InternshipBatchQuery { + ibq.ctx.Offset = &offset + return ibq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (ibq *InternshipBatchQuery) Unique(unique bool) *InternshipBatchQuery { + ibq.ctx.Unique = &unique + return ibq +} + +// Order specifies how the records should be ordered. +func (ibq *InternshipBatchQuery) Order(o ...internshipbatch.OrderOption) *InternshipBatchQuery { + ibq.order = append(ibq.order, o...) + return ibq +} + +// First returns the first InternshipBatch entity from the query. +// Returns a *NotFoundError when no InternshipBatch was found. +func (ibq *InternshipBatchQuery) First(ctx context.Context) (*InternshipBatch, error) { + nodes, err := ibq.Limit(1).All(setContextOp(ctx, ibq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{internshipbatch.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (ibq *InternshipBatchQuery) FirstX(ctx context.Context) *InternshipBatch { + node, err := ibq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first InternshipBatch ID from the query. +// Returns a *NotFoundError when no InternshipBatch ID was found. +func (ibq *InternshipBatchQuery) FirstID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = ibq.Limit(1).IDs(setContextOp(ctx, ibq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{internshipbatch.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (ibq *InternshipBatchQuery) FirstIDX(ctx context.Context) string { + id, err := ibq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single InternshipBatch entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one InternshipBatch entity is found. +// Returns a *NotFoundError when no InternshipBatch entities are found. +func (ibq *InternshipBatchQuery) Only(ctx context.Context) (*InternshipBatch, error) { + nodes, err := ibq.Limit(2).All(setContextOp(ctx, ibq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{internshipbatch.Label} + default: + return nil, &NotSingularError{internshipbatch.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (ibq *InternshipBatchQuery) OnlyX(ctx context.Context) *InternshipBatch { + node, err := ibq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only InternshipBatch ID in the query. +// Returns a *NotSingularError when more than one InternshipBatch ID is found. +// Returns a *NotFoundError when no entities are found. +func (ibq *InternshipBatchQuery) OnlyID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = ibq.Limit(2).IDs(setContextOp(ctx, ibq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{internshipbatch.Label} + default: + err = &NotSingularError{internshipbatch.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (ibq *InternshipBatchQuery) OnlyIDX(ctx context.Context) string { + id, err := ibq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of InternshipBatches. +func (ibq *InternshipBatchQuery) All(ctx context.Context) ([]*InternshipBatch, error) { + ctx = setContextOp(ctx, ibq.ctx, ent.OpQueryAll) + if err := ibq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*InternshipBatch, *InternshipBatchQuery]() + return withInterceptors[[]*InternshipBatch](ctx, ibq, qr, ibq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (ibq *InternshipBatchQuery) AllX(ctx context.Context) []*InternshipBatch { + nodes, err := ibq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of InternshipBatch IDs. +func (ibq *InternshipBatchQuery) IDs(ctx context.Context) (ids []string, err error) { + if ibq.ctx.Unique == nil && ibq.path != nil { + ibq.Unique(true) + } + ctx = setContextOp(ctx, ibq.ctx, ent.OpQueryIDs) + if err = ibq.Select(internshipbatch.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (ibq *InternshipBatchQuery) IDsX(ctx context.Context) []string { + ids, err := ibq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (ibq *InternshipBatchQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, ibq.ctx, ent.OpQueryCount) + if err := ibq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, ibq, querierCount[*InternshipBatchQuery](), ibq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (ibq *InternshipBatchQuery) CountX(ctx context.Context) int { + count, err := ibq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (ibq *InternshipBatchQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, ibq.ctx, ent.OpQueryExist) + switch _, err := ibq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (ibq *InternshipBatchQuery) ExistX(ctx context.Context) bool { + exist, err := ibq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the InternshipBatchQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (ibq *InternshipBatchQuery) Clone() *InternshipBatchQuery { + if ibq == nil { + return nil + } + return &InternshipBatchQuery{ + config: ibq.config, + ctx: ibq.ctx.Clone(), + order: append([]internshipbatch.OrderOption{}, ibq.order...), + inters: append([]Interceptor{}, ibq.inters...), + predicates: append([]predicate.InternshipBatch{}, ibq.predicates...), + // clone intermediate query. + sql: ibq.sql.Clone(), + path: ibq.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Status string `json:"status,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.InternshipBatch.Query(). +// GroupBy(internshipbatch.FieldStatus). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (ibq *InternshipBatchQuery) GroupBy(field string, fields ...string) *InternshipBatchGroupBy { + ibq.ctx.Fields = append([]string{field}, fields...) + grbuild := &InternshipBatchGroupBy{build: ibq} + grbuild.flds = &ibq.ctx.Fields + grbuild.label = internshipbatch.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Status string `json:"status,omitempty"` +// } +// +// client.InternshipBatch.Query(). +// Select(internshipbatch.FieldStatus). +// Scan(ctx, &v) +func (ibq *InternshipBatchQuery) Select(fields ...string) *InternshipBatchSelect { + ibq.ctx.Fields = append(ibq.ctx.Fields, fields...) + sbuild := &InternshipBatchSelect{InternshipBatchQuery: ibq} + sbuild.label = internshipbatch.Label + sbuild.flds, sbuild.scan = &ibq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a InternshipBatchSelect configured with the given aggregations. +func (ibq *InternshipBatchQuery) Aggregate(fns ...AggregateFunc) *InternshipBatchSelect { + return ibq.Select().Aggregate(fns...) +} + +func (ibq *InternshipBatchQuery) prepareQuery(ctx context.Context) error { + for _, inter := range ibq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, ibq); err != nil { + return err + } + } + } + for _, f := range ibq.ctx.Fields { + if !internshipbatch.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if ibq.path != nil { + prev, err := ibq.path(ctx) + if err != nil { + return err + } + ibq.sql = prev + } + return nil +} + +func (ibq *InternshipBatchQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*InternshipBatch, error) { + var ( + nodes = []*InternshipBatch{} + _spec = ibq.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*InternshipBatch).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &InternshipBatch{config: ibq.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, ibq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (ibq *InternshipBatchQuery) sqlCount(ctx context.Context) (int, error) { + _spec := ibq.querySpec() + _spec.Node.Columns = ibq.ctx.Fields + if len(ibq.ctx.Fields) > 0 { + _spec.Unique = ibq.ctx.Unique != nil && *ibq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, ibq.driver, _spec) +} + +func (ibq *InternshipBatchQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(internshipbatch.Table, internshipbatch.Columns, sqlgraph.NewFieldSpec(internshipbatch.FieldID, field.TypeString)) + _spec.From = ibq.sql + if unique := ibq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if ibq.path != nil { + _spec.Unique = true + } + if fields := ibq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, internshipbatch.FieldID) + for i := range fields { + if fields[i] != internshipbatch.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := ibq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := ibq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := ibq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := ibq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (ibq *InternshipBatchQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(ibq.driver.Dialect()) + t1 := builder.Table(internshipbatch.Table) + columns := ibq.ctx.Fields + if len(columns) == 0 { + columns = internshipbatch.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if ibq.sql != nil { + selector = ibq.sql + selector.Select(selector.Columns(columns...)...) + } + if ibq.ctx.Unique != nil && *ibq.ctx.Unique { + selector.Distinct() + } + for _, p := range ibq.predicates { + p(selector) + } + for _, p := range ibq.order { + p(selector) + } + if offset := ibq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := ibq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// InternshipBatchGroupBy is the group-by builder for InternshipBatch entities. +type InternshipBatchGroupBy struct { + selector + build *InternshipBatchQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (ibgb *InternshipBatchGroupBy) Aggregate(fns ...AggregateFunc) *InternshipBatchGroupBy { + ibgb.fns = append(ibgb.fns, fns...) + return ibgb +} + +// Scan applies the selector query and scans the result into the given value. +func (ibgb *InternshipBatchGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, ibgb.build.ctx, ent.OpQueryGroupBy) + if err := ibgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*InternshipBatchQuery, *InternshipBatchGroupBy](ctx, ibgb.build, ibgb, ibgb.build.inters, v) +} + +func (ibgb *InternshipBatchGroupBy) sqlScan(ctx context.Context, root *InternshipBatchQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(ibgb.fns)) + for _, fn := range ibgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*ibgb.flds)+len(ibgb.fns)) + for _, f := range *ibgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*ibgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := ibgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// InternshipBatchSelect is the builder for selecting fields of InternshipBatch entities. +type InternshipBatchSelect struct { + *InternshipBatchQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (ibs *InternshipBatchSelect) Aggregate(fns ...AggregateFunc) *InternshipBatchSelect { + ibs.fns = append(ibs.fns, fns...) + return ibs +} + +// Scan applies the selector query and scans the result into the given value. +func (ibs *InternshipBatchSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, ibs.ctx, ent.OpQuerySelect) + if err := ibs.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*InternshipBatchQuery, *InternshipBatchSelect](ctx, ibs.InternshipBatchQuery, ibs, ibs.inters, v) +} + +func (ibs *InternshipBatchSelect) sqlScan(ctx context.Context, root *InternshipBatchQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(ibs.fns)) + for _, fn := range ibs.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*ibs.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := ibs.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/internshipbatch_update.go b/ent/internshipbatch_update.go new file mode 100644 index 0000000..1820341 --- /dev/null +++ b/ent/internshipbatch_update.go @@ -0,0 +1,600 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/internshipbatch" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// InternshipBatchUpdate is the builder for updating InternshipBatch entities. +type InternshipBatchUpdate struct { + config + hooks []Hook + mutation *InternshipBatchMutation +} + +// Where appends a list predicates to the InternshipBatchUpdate builder. +func (ibu *InternshipBatchUpdate) Where(ps ...predicate.InternshipBatch) *InternshipBatchUpdate { + ibu.mutation.Where(ps...) + return ibu +} + +// SetStatus sets the "status" field. +func (ibu *InternshipBatchUpdate) SetStatus(s string) *InternshipBatchUpdate { + ibu.mutation.SetStatus(s) + return ibu +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (ibu *InternshipBatchUpdate) SetNillableStatus(s *string) *InternshipBatchUpdate { + if s != nil { + ibu.SetStatus(*s) + } + return ibu +} + +// SetUpdatedAt sets the "updated_at" field. +func (ibu *InternshipBatchUpdate) SetUpdatedAt(t time.Time) *InternshipBatchUpdate { + ibu.mutation.SetUpdatedAt(t) + return ibu +} + +// SetUpdatedBy sets the "updated_by" field. +func (ibu *InternshipBatchUpdate) SetUpdatedBy(s string) *InternshipBatchUpdate { + ibu.mutation.SetUpdatedBy(s) + return ibu +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (ibu *InternshipBatchUpdate) SetNillableUpdatedBy(s *string) *InternshipBatchUpdate { + if s != nil { + ibu.SetUpdatedBy(*s) + } + return ibu +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (ibu *InternshipBatchUpdate) ClearUpdatedBy() *InternshipBatchUpdate { + ibu.mutation.ClearUpdatedBy() + return ibu +} + +// SetMetadata sets the "metadata" field. +func (ibu *InternshipBatchUpdate) SetMetadata(m map[string]string) *InternshipBatchUpdate { + ibu.mutation.SetMetadata(m) + return ibu +} + +// ClearMetadata clears the value of the "metadata" field. +func (ibu *InternshipBatchUpdate) ClearMetadata() *InternshipBatchUpdate { + ibu.mutation.ClearMetadata() + return ibu +} + +// SetName sets the "name" field. +func (ibu *InternshipBatchUpdate) SetName(s string) *InternshipBatchUpdate { + ibu.mutation.SetName(s) + return ibu +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (ibu *InternshipBatchUpdate) SetNillableName(s *string) *InternshipBatchUpdate { + if s != nil { + ibu.SetName(*s) + } + return ibu +} + +// SetDescription sets the "description" field. +func (ibu *InternshipBatchUpdate) SetDescription(s string) *InternshipBatchUpdate { + ibu.mutation.SetDescription(s) + return ibu +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (ibu *InternshipBatchUpdate) SetNillableDescription(s *string) *InternshipBatchUpdate { + if s != nil { + ibu.SetDescription(*s) + } + return ibu +} + +// ClearDescription clears the value of the "description" field. +func (ibu *InternshipBatchUpdate) ClearDescription() *InternshipBatchUpdate { + ibu.mutation.ClearDescription() + return ibu +} + +// SetStartDate sets the "start_date" field. +func (ibu *InternshipBatchUpdate) SetStartDate(t time.Time) *InternshipBatchUpdate { + ibu.mutation.SetStartDate(t) + return ibu +} + +// SetNillableStartDate sets the "start_date" field if the given value is not nil. +func (ibu *InternshipBatchUpdate) SetNillableStartDate(t *time.Time) *InternshipBatchUpdate { + if t != nil { + ibu.SetStartDate(*t) + } + return ibu +} + +// ClearStartDate clears the value of the "start_date" field. +func (ibu *InternshipBatchUpdate) ClearStartDate() *InternshipBatchUpdate { + ibu.mutation.ClearStartDate() + return ibu +} + +// SetEndDate sets the "end_date" field. +func (ibu *InternshipBatchUpdate) SetEndDate(t time.Time) *InternshipBatchUpdate { + ibu.mutation.SetEndDate(t) + return ibu +} + +// SetNillableEndDate sets the "end_date" field if the given value is not nil. +func (ibu *InternshipBatchUpdate) SetNillableEndDate(t *time.Time) *InternshipBatchUpdate { + if t != nil { + ibu.SetEndDate(*t) + } + return ibu +} + +// ClearEndDate clears the value of the "end_date" field. +func (ibu *InternshipBatchUpdate) ClearEndDate() *InternshipBatchUpdate { + ibu.mutation.ClearEndDate() + return ibu +} + +// SetBatchStatus sets the "batch_status" field. +func (ibu *InternshipBatchUpdate) SetBatchStatus(s string) *InternshipBatchUpdate { + ibu.mutation.SetBatchStatus(s) + return ibu +} + +// SetNillableBatchStatus sets the "batch_status" field if the given value is not nil. +func (ibu *InternshipBatchUpdate) SetNillableBatchStatus(s *string) *InternshipBatchUpdate { + if s != nil { + ibu.SetBatchStatus(*s) + } + return ibu +} + +// Mutation returns the InternshipBatchMutation object of the builder. +func (ibu *InternshipBatchUpdate) Mutation() *InternshipBatchMutation { + return ibu.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (ibu *InternshipBatchUpdate) Save(ctx context.Context) (int, error) { + ibu.defaults() + return withHooks(ctx, ibu.sqlSave, ibu.mutation, ibu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ibu *InternshipBatchUpdate) SaveX(ctx context.Context) int { + affected, err := ibu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (ibu *InternshipBatchUpdate) Exec(ctx context.Context) error { + _, err := ibu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ibu *InternshipBatchUpdate) ExecX(ctx context.Context) { + if err := ibu.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (ibu *InternshipBatchUpdate) defaults() { + if _, ok := ibu.mutation.UpdatedAt(); !ok { + v := internshipbatch.UpdateDefaultUpdatedAt() + ibu.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (ibu *InternshipBatchUpdate) check() error { + if v, ok := ibu.mutation.Name(); ok { + if err := internshipbatch.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "InternshipBatch.name": %w`, err)} + } + } + if v, ok := ibu.mutation.BatchStatus(); ok { + if err := internshipbatch.BatchStatusValidator(v); err != nil { + return &ValidationError{Name: "batch_status", err: fmt.Errorf(`ent: validator failed for field "InternshipBatch.batch_status": %w`, err)} + } + } + return nil +} + +func (ibu *InternshipBatchUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := ibu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(internshipbatch.Table, internshipbatch.Columns, sqlgraph.NewFieldSpec(internshipbatch.FieldID, field.TypeString)) + if ps := ibu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := ibu.mutation.Status(); ok { + _spec.SetField(internshipbatch.FieldStatus, field.TypeString, value) + } + if value, ok := ibu.mutation.UpdatedAt(); ok { + _spec.SetField(internshipbatch.FieldUpdatedAt, field.TypeTime, value) + } + if ibu.mutation.CreatedByCleared() { + _spec.ClearField(internshipbatch.FieldCreatedBy, field.TypeString) + } + if value, ok := ibu.mutation.UpdatedBy(); ok { + _spec.SetField(internshipbatch.FieldUpdatedBy, field.TypeString, value) + } + if ibu.mutation.UpdatedByCleared() { + _spec.ClearField(internshipbatch.FieldUpdatedBy, field.TypeString) + } + if value, ok := ibu.mutation.Metadata(); ok { + _spec.SetField(internshipbatch.FieldMetadata, field.TypeJSON, value) + } + if ibu.mutation.MetadataCleared() { + _spec.ClearField(internshipbatch.FieldMetadata, field.TypeJSON) + } + if value, ok := ibu.mutation.Name(); ok { + _spec.SetField(internshipbatch.FieldName, field.TypeString, value) + } + if value, ok := ibu.mutation.Description(); ok { + _spec.SetField(internshipbatch.FieldDescription, field.TypeString, value) + } + if ibu.mutation.DescriptionCleared() { + _spec.ClearField(internshipbatch.FieldDescription, field.TypeString) + } + if value, ok := ibu.mutation.StartDate(); ok { + _spec.SetField(internshipbatch.FieldStartDate, field.TypeTime, value) + } + if ibu.mutation.StartDateCleared() { + _spec.ClearField(internshipbatch.FieldStartDate, field.TypeTime) + } + if value, ok := ibu.mutation.EndDate(); ok { + _spec.SetField(internshipbatch.FieldEndDate, field.TypeTime, value) + } + if ibu.mutation.EndDateCleared() { + _spec.ClearField(internshipbatch.FieldEndDate, field.TypeTime) + } + if value, ok := ibu.mutation.BatchStatus(); ok { + _spec.SetField(internshipbatch.FieldBatchStatus, field.TypeString, value) + } + if n, err = sqlgraph.UpdateNodes(ctx, ibu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{internshipbatch.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + ibu.mutation.done = true + return n, nil +} + +// InternshipBatchUpdateOne is the builder for updating a single InternshipBatch entity. +type InternshipBatchUpdateOne struct { + config + fields []string + hooks []Hook + mutation *InternshipBatchMutation +} + +// SetStatus sets the "status" field. +func (ibuo *InternshipBatchUpdateOne) SetStatus(s string) *InternshipBatchUpdateOne { + ibuo.mutation.SetStatus(s) + return ibuo +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (ibuo *InternshipBatchUpdateOne) SetNillableStatus(s *string) *InternshipBatchUpdateOne { + if s != nil { + ibuo.SetStatus(*s) + } + return ibuo +} + +// SetUpdatedAt sets the "updated_at" field. +func (ibuo *InternshipBatchUpdateOne) SetUpdatedAt(t time.Time) *InternshipBatchUpdateOne { + ibuo.mutation.SetUpdatedAt(t) + return ibuo +} + +// SetUpdatedBy sets the "updated_by" field. +func (ibuo *InternshipBatchUpdateOne) SetUpdatedBy(s string) *InternshipBatchUpdateOne { + ibuo.mutation.SetUpdatedBy(s) + return ibuo +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (ibuo *InternshipBatchUpdateOne) SetNillableUpdatedBy(s *string) *InternshipBatchUpdateOne { + if s != nil { + ibuo.SetUpdatedBy(*s) + } + return ibuo +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (ibuo *InternshipBatchUpdateOne) ClearUpdatedBy() *InternshipBatchUpdateOne { + ibuo.mutation.ClearUpdatedBy() + return ibuo +} + +// SetMetadata sets the "metadata" field. +func (ibuo *InternshipBatchUpdateOne) SetMetadata(m map[string]string) *InternshipBatchUpdateOne { + ibuo.mutation.SetMetadata(m) + return ibuo +} + +// ClearMetadata clears the value of the "metadata" field. +func (ibuo *InternshipBatchUpdateOne) ClearMetadata() *InternshipBatchUpdateOne { + ibuo.mutation.ClearMetadata() + return ibuo +} + +// SetName sets the "name" field. +func (ibuo *InternshipBatchUpdateOne) SetName(s string) *InternshipBatchUpdateOne { + ibuo.mutation.SetName(s) + return ibuo +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (ibuo *InternshipBatchUpdateOne) SetNillableName(s *string) *InternshipBatchUpdateOne { + if s != nil { + ibuo.SetName(*s) + } + return ibuo +} + +// SetDescription sets the "description" field. +func (ibuo *InternshipBatchUpdateOne) SetDescription(s string) *InternshipBatchUpdateOne { + ibuo.mutation.SetDescription(s) + return ibuo +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (ibuo *InternshipBatchUpdateOne) SetNillableDescription(s *string) *InternshipBatchUpdateOne { + if s != nil { + ibuo.SetDescription(*s) + } + return ibuo +} + +// ClearDescription clears the value of the "description" field. +func (ibuo *InternshipBatchUpdateOne) ClearDescription() *InternshipBatchUpdateOne { + ibuo.mutation.ClearDescription() + return ibuo +} + +// SetStartDate sets the "start_date" field. +func (ibuo *InternshipBatchUpdateOne) SetStartDate(t time.Time) *InternshipBatchUpdateOne { + ibuo.mutation.SetStartDate(t) + return ibuo +} + +// SetNillableStartDate sets the "start_date" field if the given value is not nil. +func (ibuo *InternshipBatchUpdateOne) SetNillableStartDate(t *time.Time) *InternshipBatchUpdateOne { + if t != nil { + ibuo.SetStartDate(*t) + } + return ibuo +} + +// ClearStartDate clears the value of the "start_date" field. +func (ibuo *InternshipBatchUpdateOne) ClearStartDate() *InternshipBatchUpdateOne { + ibuo.mutation.ClearStartDate() + return ibuo +} + +// SetEndDate sets the "end_date" field. +func (ibuo *InternshipBatchUpdateOne) SetEndDate(t time.Time) *InternshipBatchUpdateOne { + ibuo.mutation.SetEndDate(t) + return ibuo +} + +// SetNillableEndDate sets the "end_date" field if the given value is not nil. +func (ibuo *InternshipBatchUpdateOne) SetNillableEndDate(t *time.Time) *InternshipBatchUpdateOne { + if t != nil { + ibuo.SetEndDate(*t) + } + return ibuo +} + +// ClearEndDate clears the value of the "end_date" field. +func (ibuo *InternshipBatchUpdateOne) ClearEndDate() *InternshipBatchUpdateOne { + ibuo.mutation.ClearEndDate() + return ibuo +} + +// SetBatchStatus sets the "batch_status" field. +func (ibuo *InternshipBatchUpdateOne) SetBatchStatus(s string) *InternshipBatchUpdateOne { + ibuo.mutation.SetBatchStatus(s) + return ibuo +} + +// SetNillableBatchStatus sets the "batch_status" field if the given value is not nil. +func (ibuo *InternshipBatchUpdateOne) SetNillableBatchStatus(s *string) *InternshipBatchUpdateOne { + if s != nil { + ibuo.SetBatchStatus(*s) + } + return ibuo +} + +// Mutation returns the InternshipBatchMutation object of the builder. +func (ibuo *InternshipBatchUpdateOne) Mutation() *InternshipBatchMutation { + return ibuo.mutation +} + +// Where appends a list predicates to the InternshipBatchUpdate builder. +func (ibuo *InternshipBatchUpdateOne) Where(ps ...predicate.InternshipBatch) *InternshipBatchUpdateOne { + ibuo.mutation.Where(ps...) + return ibuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (ibuo *InternshipBatchUpdateOne) Select(field string, fields ...string) *InternshipBatchUpdateOne { + ibuo.fields = append([]string{field}, fields...) + return ibuo +} + +// Save executes the query and returns the updated InternshipBatch entity. +func (ibuo *InternshipBatchUpdateOne) Save(ctx context.Context) (*InternshipBatch, error) { + ibuo.defaults() + return withHooks(ctx, ibuo.sqlSave, ibuo.mutation, ibuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ibuo *InternshipBatchUpdateOne) SaveX(ctx context.Context) *InternshipBatch { + node, err := ibuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (ibuo *InternshipBatchUpdateOne) Exec(ctx context.Context) error { + _, err := ibuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ibuo *InternshipBatchUpdateOne) ExecX(ctx context.Context) { + if err := ibuo.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (ibuo *InternshipBatchUpdateOne) defaults() { + if _, ok := ibuo.mutation.UpdatedAt(); !ok { + v := internshipbatch.UpdateDefaultUpdatedAt() + ibuo.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (ibuo *InternshipBatchUpdateOne) check() error { + if v, ok := ibuo.mutation.Name(); ok { + if err := internshipbatch.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "InternshipBatch.name": %w`, err)} + } + } + if v, ok := ibuo.mutation.BatchStatus(); ok { + if err := internshipbatch.BatchStatusValidator(v); err != nil { + return &ValidationError{Name: "batch_status", err: fmt.Errorf(`ent: validator failed for field "InternshipBatch.batch_status": %w`, err)} + } + } + return nil +} + +func (ibuo *InternshipBatchUpdateOne) sqlSave(ctx context.Context) (_node *InternshipBatch, err error) { + if err := ibuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(internshipbatch.Table, internshipbatch.Columns, sqlgraph.NewFieldSpec(internshipbatch.FieldID, field.TypeString)) + id, ok := ibuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "InternshipBatch.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := ibuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, internshipbatch.FieldID) + for _, f := range fields { + if !internshipbatch.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != internshipbatch.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := ibuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := ibuo.mutation.Status(); ok { + _spec.SetField(internshipbatch.FieldStatus, field.TypeString, value) + } + if value, ok := ibuo.mutation.UpdatedAt(); ok { + _spec.SetField(internshipbatch.FieldUpdatedAt, field.TypeTime, value) + } + if ibuo.mutation.CreatedByCleared() { + _spec.ClearField(internshipbatch.FieldCreatedBy, field.TypeString) + } + if value, ok := ibuo.mutation.UpdatedBy(); ok { + _spec.SetField(internshipbatch.FieldUpdatedBy, field.TypeString, value) + } + if ibuo.mutation.UpdatedByCleared() { + _spec.ClearField(internshipbatch.FieldUpdatedBy, field.TypeString) + } + if value, ok := ibuo.mutation.Metadata(); ok { + _spec.SetField(internshipbatch.FieldMetadata, field.TypeJSON, value) + } + if ibuo.mutation.MetadataCleared() { + _spec.ClearField(internshipbatch.FieldMetadata, field.TypeJSON) + } + if value, ok := ibuo.mutation.Name(); ok { + _spec.SetField(internshipbatch.FieldName, field.TypeString, value) + } + if value, ok := ibuo.mutation.Description(); ok { + _spec.SetField(internshipbatch.FieldDescription, field.TypeString, value) + } + if ibuo.mutation.DescriptionCleared() { + _spec.ClearField(internshipbatch.FieldDescription, field.TypeString) + } + if value, ok := ibuo.mutation.StartDate(); ok { + _spec.SetField(internshipbatch.FieldStartDate, field.TypeTime, value) + } + if ibuo.mutation.StartDateCleared() { + _spec.ClearField(internshipbatch.FieldStartDate, field.TypeTime) + } + if value, ok := ibuo.mutation.EndDate(); ok { + _spec.SetField(internshipbatch.FieldEndDate, field.TypeTime, value) + } + if ibuo.mutation.EndDateCleared() { + _spec.ClearField(internshipbatch.FieldEndDate, field.TypeTime) + } + if value, ok := ibuo.mutation.BatchStatus(); ok { + _spec.SetField(internshipbatch.FieldBatchStatus, field.TypeString, value) + } + _node = &InternshipBatch{config: ibuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, ibuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{internshipbatch.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + ibuo.mutation.done = true + return _node, nil +} diff --git a/ent/internshipenrollment.go b/ent/internshipenrollment.go new file mode 100644 index 0000000..319c984 --- /dev/null +++ b/ent/internshipenrollment.go @@ -0,0 +1,304 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/internshipenrollment" + "github.com/omkar273/codegeeky/internal/types" +) + +// InternshipEnrollment is the model entity for the InternshipEnrollment schema. +type InternshipEnrollment struct { + config `json:"-"` + // ID of the ent. + ID string `json:"id,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // UpdatedBy holds the value of the "updated_by" field. + UpdatedBy string `json:"updated_by,omitempty"` + // Metadata holds the value of the "metadata" field. + Metadata map[string]string `json:"metadata,omitempty"` + // UserID holds the value of the "user_id" field. + UserID string `json:"user_id,omitempty"` + // InternshipID holds the value of the "internship_id" field. + InternshipID string `json:"internship_id,omitempty"` + // InternshipBatchID holds the value of the "internship_batch_id" field. + InternshipBatchID string `json:"internship_batch_id,omitempty"` + // EnrollmentStatus holds the value of the "enrollment_status" field. + EnrollmentStatus types.InternshipEnrollmentStatus `json:"enrollment_status,omitempty"` + // PaymentStatus holds the value of the "payment_status" field. + PaymentStatus types.PaymentStatus `json:"payment_status,omitempty"` + // EnrolledAt holds the value of the "enrolled_at" field. + EnrolledAt *time.Time `json:"enrolled_at,omitempty"` + // PaymentID holds the value of the "payment_id" field. + PaymentID *string `json:"payment_id,omitempty"` + // RefundedAt holds the value of the "refunded_at" field. + RefundedAt *time.Time `json:"refunded_at,omitempty"` + // CancellationReason holds the value of the "cancellation_reason" field. + CancellationReason *string `json:"cancellation_reason,omitempty"` + // RefundReason holds the value of the "refund_reason" field. + RefundReason *string `json:"refund_reason,omitempty"` + // IdempotencyKey holds the value of the "idempotency_key" field. + IdempotencyKey *string `json:"idempotency_key,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*InternshipEnrollment) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case internshipenrollment.FieldMetadata: + values[i] = new([]byte) + case internshipenrollment.FieldID, internshipenrollment.FieldStatus, internshipenrollment.FieldCreatedBy, internshipenrollment.FieldUpdatedBy, internshipenrollment.FieldUserID, internshipenrollment.FieldInternshipID, internshipenrollment.FieldInternshipBatchID, internshipenrollment.FieldEnrollmentStatus, internshipenrollment.FieldPaymentStatus, internshipenrollment.FieldPaymentID, internshipenrollment.FieldCancellationReason, internshipenrollment.FieldRefundReason, internshipenrollment.FieldIdempotencyKey: + values[i] = new(sql.NullString) + case internshipenrollment.FieldCreatedAt, internshipenrollment.FieldUpdatedAt, internshipenrollment.FieldEnrolledAt, internshipenrollment.FieldRefundedAt: + values[i] = new(sql.NullTime) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the InternshipEnrollment fields. +func (ie *InternshipEnrollment) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case internshipenrollment.FieldID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value.Valid { + ie.ID = value.String + } + case internshipenrollment.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + ie.Status = value.String + } + case internshipenrollment.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + ie.CreatedAt = value.Time + } + case internshipenrollment.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + ie.UpdatedAt = value.Time + } + case internshipenrollment.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + ie.CreatedBy = value.String + } + case internshipenrollment.FieldUpdatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field updated_by", values[i]) + } else if value.Valid { + ie.UpdatedBy = value.String + } + case internshipenrollment.FieldMetadata: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field metadata", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &ie.Metadata); err != nil { + return fmt.Errorf("unmarshal field metadata: %w", err) + } + } + case internshipenrollment.FieldUserID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field user_id", values[i]) + } else if value.Valid { + ie.UserID = value.String + } + case internshipenrollment.FieldInternshipID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field internship_id", values[i]) + } else if value.Valid { + ie.InternshipID = value.String + } + case internshipenrollment.FieldInternshipBatchID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field internship_batch_id", values[i]) + } else if value.Valid { + ie.InternshipBatchID = value.String + } + case internshipenrollment.FieldEnrollmentStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field enrollment_status", values[i]) + } else if value.Valid { + ie.EnrollmentStatus = types.InternshipEnrollmentStatus(value.String) + } + case internshipenrollment.FieldPaymentStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field payment_status", values[i]) + } else if value.Valid { + ie.PaymentStatus = types.PaymentStatus(value.String) + } + case internshipenrollment.FieldEnrolledAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field enrolled_at", values[i]) + } else if value.Valid { + ie.EnrolledAt = new(time.Time) + *ie.EnrolledAt = value.Time + } + case internshipenrollment.FieldPaymentID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field payment_id", values[i]) + } else if value.Valid { + ie.PaymentID = new(string) + *ie.PaymentID = value.String + } + case internshipenrollment.FieldRefundedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field refunded_at", values[i]) + } else if value.Valid { + ie.RefundedAt = new(time.Time) + *ie.RefundedAt = value.Time + } + case internshipenrollment.FieldCancellationReason: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field cancellation_reason", values[i]) + } else if value.Valid { + ie.CancellationReason = new(string) + *ie.CancellationReason = value.String + } + case internshipenrollment.FieldRefundReason: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field refund_reason", values[i]) + } else if value.Valid { + ie.RefundReason = new(string) + *ie.RefundReason = value.String + } + case internshipenrollment.FieldIdempotencyKey: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field idempotency_key", values[i]) + } else if value.Valid { + ie.IdempotencyKey = new(string) + *ie.IdempotencyKey = value.String + } + default: + ie.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the InternshipEnrollment. +// This includes values selected through modifiers, order, etc. +func (ie *InternshipEnrollment) Value(name string) (ent.Value, error) { + return ie.selectValues.Get(name) +} + +// Update returns a builder for updating this InternshipEnrollment. +// Note that you need to call InternshipEnrollment.Unwrap() before calling this method if this InternshipEnrollment +// was returned from a transaction, and the transaction was committed or rolled back. +func (ie *InternshipEnrollment) Update() *InternshipEnrollmentUpdateOne { + return NewInternshipEnrollmentClient(ie.config).UpdateOne(ie) +} + +// Unwrap unwraps the InternshipEnrollment entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (ie *InternshipEnrollment) Unwrap() *InternshipEnrollment { + _tx, ok := ie.config.driver.(*txDriver) + if !ok { + panic("ent: InternshipEnrollment is not a transactional entity") + } + ie.config.driver = _tx.drv + return ie +} + +// String implements the fmt.Stringer. +func (ie *InternshipEnrollment) String() string { + var builder strings.Builder + builder.WriteString("InternshipEnrollment(") + builder.WriteString(fmt.Sprintf("id=%v, ", ie.ID)) + builder.WriteString("status=") + builder.WriteString(ie.Status) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(ie.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(ie.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(ie.CreatedBy) + builder.WriteString(", ") + builder.WriteString("updated_by=") + builder.WriteString(ie.UpdatedBy) + builder.WriteString(", ") + builder.WriteString("metadata=") + builder.WriteString(fmt.Sprintf("%v", ie.Metadata)) + builder.WriteString(", ") + builder.WriteString("user_id=") + builder.WriteString(ie.UserID) + builder.WriteString(", ") + builder.WriteString("internship_id=") + builder.WriteString(ie.InternshipID) + builder.WriteString(", ") + builder.WriteString("internship_batch_id=") + builder.WriteString(ie.InternshipBatchID) + builder.WriteString(", ") + builder.WriteString("enrollment_status=") + builder.WriteString(fmt.Sprintf("%v", ie.EnrollmentStatus)) + builder.WriteString(", ") + builder.WriteString("payment_status=") + builder.WriteString(fmt.Sprintf("%v", ie.PaymentStatus)) + builder.WriteString(", ") + if v := ie.EnrolledAt; v != nil { + builder.WriteString("enrolled_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := ie.PaymentID; v != nil { + builder.WriteString("payment_id=") + builder.WriteString(*v) + } + builder.WriteString(", ") + if v := ie.RefundedAt; v != nil { + builder.WriteString("refunded_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := ie.CancellationReason; v != nil { + builder.WriteString("cancellation_reason=") + builder.WriteString(*v) + } + builder.WriteString(", ") + if v := ie.RefundReason; v != nil { + builder.WriteString("refund_reason=") + builder.WriteString(*v) + } + builder.WriteString(", ") + if v := ie.IdempotencyKey; v != nil { + builder.WriteString("idempotency_key=") + builder.WriteString(*v) + } + builder.WriteByte(')') + return builder.String() +} + +// InternshipEnrollments is a parsable slice of InternshipEnrollment. +type InternshipEnrollments []*InternshipEnrollment diff --git a/ent/internshipenrollment/internshipenrollment.go b/ent/internshipenrollment/internshipenrollment.go new file mode 100644 index 0000000..6b0a585 --- /dev/null +++ b/ent/internshipenrollment/internshipenrollment.go @@ -0,0 +1,202 @@ +// Code generated by ent, DO NOT EDIT. + +package internshipenrollment + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/internal/types" +) + +const ( + // Label holds the string label denoting the internshipenrollment type in the database. + Label = "internship_enrollment" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldUpdatedBy holds the string denoting the updated_by field in the database. + FieldUpdatedBy = "updated_by" + // FieldMetadata holds the string denoting the metadata field in the database. + FieldMetadata = "metadata" + // FieldUserID holds the string denoting the user_id field in the database. + FieldUserID = "user_id" + // FieldInternshipID holds the string denoting the internship_id field in the database. + FieldInternshipID = "internship_id" + // FieldInternshipBatchID holds the string denoting the internship_batch_id field in the database. + FieldInternshipBatchID = "internship_batch_id" + // FieldEnrollmentStatus holds the string denoting the enrollment_status field in the database. + FieldEnrollmentStatus = "enrollment_status" + // FieldPaymentStatus holds the string denoting the payment_status field in the database. + FieldPaymentStatus = "payment_status" + // FieldEnrolledAt holds the string denoting the enrolled_at field in the database. + FieldEnrolledAt = "enrolled_at" + // FieldPaymentID holds the string denoting the payment_id field in the database. + FieldPaymentID = "payment_id" + // FieldRefundedAt holds the string denoting the refunded_at field in the database. + FieldRefundedAt = "refunded_at" + // FieldCancellationReason holds the string denoting the cancellation_reason field in the database. + FieldCancellationReason = "cancellation_reason" + // FieldRefundReason holds the string denoting the refund_reason field in the database. + FieldRefundReason = "refund_reason" + // FieldIdempotencyKey holds the string denoting the idempotency_key field in the database. + FieldIdempotencyKey = "idempotency_key" + // Table holds the table name of the internshipenrollment in the database. + Table = "internship_enrollments" +) + +// Columns holds all SQL columns for internshipenrollment fields. +var Columns = []string{ + FieldID, + FieldStatus, + FieldCreatedAt, + FieldUpdatedAt, + FieldCreatedBy, + FieldUpdatedBy, + FieldMetadata, + FieldUserID, + FieldInternshipID, + FieldInternshipBatchID, + FieldEnrollmentStatus, + FieldPaymentStatus, + FieldEnrolledAt, + FieldPaymentID, + FieldRefundedAt, + FieldCancellationReason, + FieldRefundReason, + FieldIdempotencyKey, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultMetadata holds the default value on creation for the "metadata" field. + DefaultMetadata map[string]string + // UserIDValidator is a validator for the "user_id" field. It is called by the builders before save. + UserIDValidator func(string) error + // InternshipIDValidator is a validator for the "internship_id" field. It is called by the builders before save. + InternshipIDValidator func(string) error + // InternshipBatchIDValidator is a validator for the "internship_batch_id" field. It is called by the builders before save. + InternshipBatchIDValidator func(string) error + // DefaultEnrollmentStatus holds the default value on creation for the "enrollment_status" field. + DefaultEnrollmentStatus types.InternshipEnrollmentStatus + // EnrollmentStatusValidator is a validator for the "enrollment_status" field. It is called by the builders before save. + EnrollmentStatusValidator func(string) error + // DefaultPaymentStatus holds the default value on creation for the "payment_status" field. + DefaultPaymentStatus types.PaymentStatus + // PaymentStatusValidator is a validator for the "payment_status" field. It is called by the builders before save. + PaymentStatusValidator func(string) error + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() string +) + +// OrderOption defines the ordering options for the InternshipEnrollment queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByUpdatedBy orders the results by the updated_by field. +func ByUpdatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedBy, opts...).ToFunc() +} + +// ByUserID orders the results by the user_id field. +func ByUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUserID, opts...).ToFunc() +} + +// ByInternshipID orders the results by the internship_id field. +func ByInternshipID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInternshipID, opts...).ToFunc() +} + +// ByInternshipBatchID orders the results by the internship_batch_id field. +func ByInternshipBatchID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInternshipBatchID, opts...).ToFunc() +} + +// ByEnrollmentStatus orders the results by the enrollment_status field. +func ByEnrollmentStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEnrollmentStatus, opts...).ToFunc() +} + +// ByPaymentStatus orders the results by the payment_status field. +func ByPaymentStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPaymentStatus, opts...).ToFunc() +} + +// ByEnrolledAt orders the results by the enrolled_at field. +func ByEnrolledAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEnrolledAt, opts...).ToFunc() +} + +// ByPaymentID orders the results by the payment_id field. +func ByPaymentID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPaymentID, opts...).ToFunc() +} + +// ByRefundedAt orders the results by the refunded_at field. +func ByRefundedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRefundedAt, opts...).ToFunc() +} + +// ByCancellationReason orders the results by the cancellation_reason field. +func ByCancellationReason(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCancellationReason, opts...).ToFunc() +} + +// ByRefundReason orders the results by the refund_reason field. +func ByRefundReason(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRefundReason, opts...).ToFunc() +} + +// ByIdempotencyKey orders the results by the idempotency_key field. +func ByIdempotencyKey(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldIdempotencyKey, opts...).ToFunc() +} diff --git a/ent/internshipenrollment/where.go b/ent/internshipenrollment/where.go new file mode 100644 index 0000000..6bef520 --- /dev/null +++ b/ent/internshipenrollment/where.go @@ -0,0 +1,1231 @@ +// Code generated by ent, DO NOT EDIT. + +package internshipenrollment + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/predicate" + "github.com/omkar273/codegeeky/internal/types" +) + +// ID filters vertices based on their ID field. +func ID(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldID, id)) +} + +// IDEqualFold applies the EqualFold predicate on the ID field. +func IDEqualFold(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldID, id)) +} + +// IDContainsFold applies the ContainsFold predicate on the ID field. +func IDContainsFold(id string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldID, id)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldStatus, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldCreatedBy, v)) +} + +// UpdatedBy applies equality check predicate on the "updated_by" field. It's identical to UpdatedByEQ. +func UpdatedBy(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ. +func UserID(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldUserID, v)) +} + +// InternshipID applies equality check predicate on the "internship_id" field. It's identical to InternshipIDEQ. +func InternshipID(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldInternshipID, v)) +} + +// InternshipBatchID applies equality check predicate on the "internship_batch_id" field. It's identical to InternshipBatchIDEQ. +func InternshipBatchID(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldInternshipBatchID, v)) +} + +// EnrollmentStatus applies equality check predicate on the "enrollment_status" field. It's identical to EnrollmentStatusEQ. +func EnrollmentStatus(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldEQ(FieldEnrollmentStatus, vc)) +} + +// PaymentStatus applies equality check predicate on the "payment_status" field. It's identical to PaymentStatusEQ. +func PaymentStatus(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldEQ(FieldPaymentStatus, vc)) +} + +// EnrolledAt applies equality check predicate on the "enrolled_at" field. It's identical to EnrolledAtEQ. +func EnrolledAt(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldEnrolledAt, v)) +} + +// PaymentID applies equality check predicate on the "payment_id" field. It's identical to PaymentIDEQ. +func PaymentID(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldPaymentID, v)) +} + +// RefundedAt applies equality check predicate on the "refunded_at" field. It's identical to RefundedAtEQ. +func RefundedAt(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldRefundedAt, v)) +} + +// CancellationReason applies equality check predicate on the "cancellation_reason" field. It's identical to CancellationReasonEQ. +func CancellationReason(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldCancellationReason, v)) +} + +// RefundReason applies equality check predicate on the "refund_reason" field. It's identical to RefundReasonEQ. +func RefundReason(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldRefundReason, v)) +} + +// IdempotencyKey applies equality check predicate on the "idempotency_key" field. It's identical to IdempotencyKeyEQ. +func IdempotencyKey(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldIdempotencyKey, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldStatus, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// UpdatedByEQ applies the EQ predicate on the "updated_by" field. +func UpdatedByEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UpdatedByNEQ applies the NEQ predicate on the "updated_by" field. +func UpdatedByNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldUpdatedBy, v)) +} + +// UpdatedByIn applies the In predicate on the "updated_by" field. +func UpdatedByIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByNotIn applies the NotIn predicate on the "updated_by" field. +func UpdatedByNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByGT applies the GT predicate on the "updated_by" field. +func UpdatedByGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldUpdatedBy, v)) +} + +// UpdatedByGTE applies the GTE predicate on the "updated_by" field. +func UpdatedByGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldUpdatedBy, v)) +} + +// UpdatedByLT applies the LT predicate on the "updated_by" field. +func UpdatedByLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldUpdatedBy, v)) +} + +// UpdatedByLTE applies the LTE predicate on the "updated_by" field. +func UpdatedByLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldUpdatedBy, v)) +} + +// UpdatedByContains applies the Contains predicate on the "updated_by" field. +func UpdatedByContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldUpdatedBy, v)) +} + +// UpdatedByHasPrefix applies the HasPrefix predicate on the "updated_by" field. +func UpdatedByHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldUpdatedBy, v)) +} + +// UpdatedByHasSuffix applies the HasSuffix predicate on the "updated_by" field. +func UpdatedByHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldUpdatedBy, v)) +} + +// UpdatedByIsNil applies the IsNil predicate on the "updated_by" field. +func UpdatedByIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldUpdatedBy)) +} + +// UpdatedByNotNil applies the NotNil predicate on the "updated_by" field. +func UpdatedByNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldUpdatedBy)) +} + +// UpdatedByEqualFold applies the EqualFold predicate on the "updated_by" field. +func UpdatedByEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldUpdatedBy, v)) +} + +// UpdatedByContainsFold applies the ContainsFold predicate on the "updated_by" field. +func UpdatedByContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldUpdatedBy, v)) +} + +// MetadataIsNil applies the IsNil predicate on the "metadata" field. +func MetadataIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldMetadata)) +} + +// MetadataNotNil applies the NotNil predicate on the "metadata" field. +func MetadataNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldMetadata)) +} + +// UserIDEQ applies the EQ predicate on the "user_id" field. +func UserIDEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldUserID, v)) +} + +// UserIDNEQ applies the NEQ predicate on the "user_id" field. +func UserIDNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldUserID, v)) +} + +// UserIDIn applies the In predicate on the "user_id" field. +func UserIDIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldUserID, vs...)) +} + +// UserIDNotIn applies the NotIn predicate on the "user_id" field. +func UserIDNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldUserID, vs...)) +} + +// UserIDGT applies the GT predicate on the "user_id" field. +func UserIDGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldUserID, v)) +} + +// UserIDGTE applies the GTE predicate on the "user_id" field. +func UserIDGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldUserID, v)) +} + +// UserIDLT applies the LT predicate on the "user_id" field. +func UserIDLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldUserID, v)) +} + +// UserIDLTE applies the LTE predicate on the "user_id" field. +func UserIDLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldUserID, v)) +} + +// UserIDContains applies the Contains predicate on the "user_id" field. +func UserIDContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldUserID, v)) +} + +// UserIDHasPrefix applies the HasPrefix predicate on the "user_id" field. +func UserIDHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldUserID, v)) +} + +// UserIDHasSuffix applies the HasSuffix predicate on the "user_id" field. +func UserIDHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldUserID, v)) +} + +// UserIDEqualFold applies the EqualFold predicate on the "user_id" field. +func UserIDEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldUserID, v)) +} + +// UserIDContainsFold applies the ContainsFold predicate on the "user_id" field. +func UserIDContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldUserID, v)) +} + +// InternshipIDEQ applies the EQ predicate on the "internship_id" field. +func InternshipIDEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldInternshipID, v)) +} + +// InternshipIDNEQ applies the NEQ predicate on the "internship_id" field. +func InternshipIDNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldInternshipID, v)) +} + +// InternshipIDIn applies the In predicate on the "internship_id" field. +func InternshipIDIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldInternshipID, vs...)) +} + +// InternshipIDNotIn applies the NotIn predicate on the "internship_id" field. +func InternshipIDNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldInternshipID, vs...)) +} + +// InternshipIDGT applies the GT predicate on the "internship_id" field. +func InternshipIDGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldInternshipID, v)) +} + +// InternshipIDGTE applies the GTE predicate on the "internship_id" field. +func InternshipIDGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldInternshipID, v)) +} + +// InternshipIDLT applies the LT predicate on the "internship_id" field. +func InternshipIDLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldInternshipID, v)) +} + +// InternshipIDLTE applies the LTE predicate on the "internship_id" field. +func InternshipIDLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldInternshipID, v)) +} + +// InternshipIDContains applies the Contains predicate on the "internship_id" field. +func InternshipIDContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldInternshipID, v)) +} + +// InternshipIDHasPrefix applies the HasPrefix predicate on the "internship_id" field. +func InternshipIDHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldInternshipID, v)) +} + +// InternshipIDHasSuffix applies the HasSuffix predicate on the "internship_id" field. +func InternshipIDHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldInternshipID, v)) +} + +// InternshipIDEqualFold applies the EqualFold predicate on the "internship_id" field. +func InternshipIDEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldInternshipID, v)) +} + +// InternshipIDContainsFold applies the ContainsFold predicate on the "internship_id" field. +func InternshipIDContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldInternshipID, v)) +} + +// InternshipBatchIDEQ applies the EQ predicate on the "internship_batch_id" field. +func InternshipBatchIDEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDNEQ applies the NEQ predicate on the "internship_batch_id" field. +func InternshipBatchIDNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDIn applies the In predicate on the "internship_batch_id" field. +func InternshipBatchIDIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldInternshipBatchID, vs...)) +} + +// InternshipBatchIDNotIn applies the NotIn predicate on the "internship_batch_id" field. +func InternshipBatchIDNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldInternshipBatchID, vs...)) +} + +// InternshipBatchIDGT applies the GT predicate on the "internship_batch_id" field. +func InternshipBatchIDGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDGTE applies the GTE predicate on the "internship_batch_id" field. +func InternshipBatchIDGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDLT applies the LT predicate on the "internship_batch_id" field. +func InternshipBatchIDLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDLTE applies the LTE predicate on the "internship_batch_id" field. +func InternshipBatchIDLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDContains applies the Contains predicate on the "internship_batch_id" field. +func InternshipBatchIDContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDHasPrefix applies the HasPrefix predicate on the "internship_batch_id" field. +func InternshipBatchIDHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDHasSuffix applies the HasSuffix predicate on the "internship_batch_id" field. +func InternshipBatchIDHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDEqualFold applies the EqualFold predicate on the "internship_batch_id" field. +func InternshipBatchIDEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldInternshipBatchID, v)) +} + +// InternshipBatchIDContainsFold applies the ContainsFold predicate on the "internship_batch_id" field. +func InternshipBatchIDContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldInternshipBatchID, v)) +} + +// EnrollmentStatusEQ applies the EQ predicate on the "enrollment_status" field. +func EnrollmentStatusEQ(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldEQ(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusNEQ applies the NEQ predicate on the "enrollment_status" field. +func EnrollmentStatusNEQ(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusIn applies the In predicate on the "enrollment_status" field. +func EnrollmentStatusIn(vs ...types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + v := make([]any, len(vs)) + for i := range v { + v[i] = string(vs[i]) + } + return predicate.InternshipEnrollment(sql.FieldIn(FieldEnrollmentStatus, v...)) +} + +// EnrollmentStatusNotIn applies the NotIn predicate on the "enrollment_status" field. +func EnrollmentStatusNotIn(vs ...types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + v := make([]any, len(vs)) + for i := range v { + v[i] = string(vs[i]) + } + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldEnrollmentStatus, v...)) +} + +// EnrollmentStatusGT applies the GT predicate on the "enrollment_status" field. +func EnrollmentStatusGT(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldGT(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusGTE applies the GTE predicate on the "enrollment_status" field. +func EnrollmentStatusGTE(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldGTE(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusLT applies the LT predicate on the "enrollment_status" field. +func EnrollmentStatusLT(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldLT(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusLTE applies the LTE predicate on the "enrollment_status" field. +func EnrollmentStatusLTE(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldLTE(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusContains applies the Contains predicate on the "enrollment_status" field. +func EnrollmentStatusContains(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldContains(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusHasPrefix applies the HasPrefix predicate on the "enrollment_status" field. +func EnrollmentStatusHasPrefix(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusHasSuffix applies the HasSuffix predicate on the "enrollment_status" field. +func EnrollmentStatusHasSuffix(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusEqualFold applies the EqualFold predicate on the "enrollment_status" field. +func EnrollmentStatusEqualFold(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldEnrollmentStatus, vc)) +} + +// EnrollmentStatusContainsFold applies the ContainsFold predicate on the "enrollment_status" field. +func EnrollmentStatusContainsFold(v types.InternshipEnrollmentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldEnrollmentStatus, vc)) +} + +// PaymentStatusEQ applies the EQ predicate on the "payment_status" field. +func PaymentStatusEQ(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldEQ(FieldPaymentStatus, vc)) +} + +// PaymentStatusNEQ applies the NEQ predicate on the "payment_status" field. +func PaymentStatusNEQ(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldPaymentStatus, vc)) +} + +// PaymentStatusIn applies the In predicate on the "payment_status" field. +func PaymentStatusIn(vs ...types.PaymentStatus) predicate.InternshipEnrollment { + v := make([]any, len(vs)) + for i := range v { + v[i] = string(vs[i]) + } + return predicate.InternshipEnrollment(sql.FieldIn(FieldPaymentStatus, v...)) +} + +// PaymentStatusNotIn applies the NotIn predicate on the "payment_status" field. +func PaymentStatusNotIn(vs ...types.PaymentStatus) predicate.InternshipEnrollment { + v := make([]any, len(vs)) + for i := range v { + v[i] = string(vs[i]) + } + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldPaymentStatus, v...)) +} + +// PaymentStatusGT applies the GT predicate on the "payment_status" field. +func PaymentStatusGT(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldGT(FieldPaymentStatus, vc)) +} + +// PaymentStatusGTE applies the GTE predicate on the "payment_status" field. +func PaymentStatusGTE(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldGTE(FieldPaymentStatus, vc)) +} + +// PaymentStatusLT applies the LT predicate on the "payment_status" field. +func PaymentStatusLT(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldLT(FieldPaymentStatus, vc)) +} + +// PaymentStatusLTE applies the LTE predicate on the "payment_status" field. +func PaymentStatusLTE(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldLTE(FieldPaymentStatus, vc)) +} + +// PaymentStatusContains applies the Contains predicate on the "payment_status" field. +func PaymentStatusContains(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldContains(FieldPaymentStatus, vc)) +} + +// PaymentStatusHasPrefix applies the HasPrefix predicate on the "payment_status" field. +func PaymentStatusHasPrefix(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldPaymentStatus, vc)) +} + +// PaymentStatusHasSuffix applies the HasSuffix predicate on the "payment_status" field. +func PaymentStatusHasSuffix(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldPaymentStatus, vc)) +} + +// PaymentStatusEqualFold applies the EqualFold predicate on the "payment_status" field. +func PaymentStatusEqualFold(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldPaymentStatus, vc)) +} + +// PaymentStatusContainsFold applies the ContainsFold predicate on the "payment_status" field. +func PaymentStatusContainsFold(v types.PaymentStatus) predicate.InternshipEnrollment { + vc := string(v) + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldPaymentStatus, vc)) +} + +// EnrolledAtEQ applies the EQ predicate on the "enrolled_at" field. +func EnrolledAtEQ(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldEnrolledAt, v)) +} + +// EnrolledAtNEQ applies the NEQ predicate on the "enrolled_at" field. +func EnrolledAtNEQ(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldEnrolledAt, v)) +} + +// EnrolledAtIn applies the In predicate on the "enrolled_at" field. +func EnrolledAtIn(vs ...time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldEnrolledAt, vs...)) +} + +// EnrolledAtNotIn applies the NotIn predicate on the "enrolled_at" field. +func EnrolledAtNotIn(vs ...time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldEnrolledAt, vs...)) +} + +// EnrolledAtGT applies the GT predicate on the "enrolled_at" field. +func EnrolledAtGT(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldEnrolledAt, v)) +} + +// EnrolledAtGTE applies the GTE predicate on the "enrolled_at" field. +func EnrolledAtGTE(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldEnrolledAt, v)) +} + +// EnrolledAtLT applies the LT predicate on the "enrolled_at" field. +func EnrolledAtLT(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldEnrolledAt, v)) +} + +// EnrolledAtLTE applies the LTE predicate on the "enrolled_at" field. +func EnrolledAtLTE(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldEnrolledAt, v)) +} + +// EnrolledAtIsNil applies the IsNil predicate on the "enrolled_at" field. +func EnrolledAtIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldEnrolledAt)) +} + +// EnrolledAtNotNil applies the NotNil predicate on the "enrolled_at" field. +func EnrolledAtNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldEnrolledAt)) +} + +// PaymentIDEQ applies the EQ predicate on the "payment_id" field. +func PaymentIDEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldPaymentID, v)) +} + +// PaymentIDNEQ applies the NEQ predicate on the "payment_id" field. +func PaymentIDNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldPaymentID, v)) +} + +// PaymentIDIn applies the In predicate on the "payment_id" field. +func PaymentIDIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldPaymentID, vs...)) +} + +// PaymentIDNotIn applies the NotIn predicate on the "payment_id" field. +func PaymentIDNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldPaymentID, vs...)) +} + +// PaymentIDGT applies the GT predicate on the "payment_id" field. +func PaymentIDGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldPaymentID, v)) +} + +// PaymentIDGTE applies the GTE predicate on the "payment_id" field. +func PaymentIDGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldPaymentID, v)) +} + +// PaymentIDLT applies the LT predicate on the "payment_id" field. +func PaymentIDLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldPaymentID, v)) +} + +// PaymentIDLTE applies the LTE predicate on the "payment_id" field. +func PaymentIDLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldPaymentID, v)) +} + +// PaymentIDContains applies the Contains predicate on the "payment_id" field. +func PaymentIDContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldPaymentID, v)) +} + +// PaymentIDHasPrefix applies the HasPrefix predicate on the "payment_id" field. +func PaymentIDHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldPaymentID, v)) +} + +// PaymentIDHasSuffix applies the HasSuffix predicate on the "payment_id" field. +func PaymentIDHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldPaymentID, v)) +} + +// PaymentIDIsNil applies the IsNil predicate on the "payment_id" field. +func PaymentIDIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldPaymentID)) +} + +// PaymentIDNotNil applies the NotNil predicate on the "payment_id" field. +func PaymentIDNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldPaymentID)) +} + +// PaymentIDEqualFold applies the EqualFold predicate on the "payment_id" field. +func PaymentIDEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldPaymentID, v)) +} + +// PaymentIDContainsFold applies the ContainsFold predicate on the "payment_id" field. +func PaymentIDContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldPaymentID, v)) +} + +// RefundedAtEQ applies the EQ predicate on the "refunded_at" field. +func RefundedAtEQ(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldRefundedAt, v)) +} + +// RefundedAtNEQ applies the NEQ predicate on the "refunded_at" field. +func RefundedAtNEQ(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldRefundedAt, v)) +} + +// RefundedAtIn applies the In predicate on the "refunded_at" field. +func RefundedAtIn(vs ...time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldRefundedAt, vs...)) +} + +// RefundedAtNotIn applies the NotIn predicate on the "refunded_at" field. +func RefundedAtNotIn(vs ...time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldRefundedAt, vs...)) +} + +// RefundedAtGT applies the GT predicate on the "refunded_at" field. +func RefundedAtGT(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldRefundedAt, v)) +} + +// RefundedAtGTE applies the GTE predicate on the "refunded_at" field. +func RefundedAtGTE(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldRefundedAt, v)) +} + +// RefundedAtLT applies the LT predicate on the "refunded_at" field. +func RefundedAtLT(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldRefundedAt, v)) +} + +// RefundedAtLTE applies the LTE predicate on the "refunded_at" field. +func RefundedAtLTE(v time.Time) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldRefundedAt, v)) +} + +// RefundedAtIsNil applies the IsNil predicate on the "refunded_at" field. +func RefundedAtIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldRefundedAt)) +} + +// RefundedAtNotNil applies the NotNil predicate on the "refunded_at" field. +func RefundedAtNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldRefundedAt)) +} + +// CancellationReasonEQ applies the EQ predicate on the "cancellation_reason" field. +func CancellationReasonEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldCancellationReason, v)) +} + +// CancellationReasonNEQ applies the NEQ predicate on the "cancellation_reason" field. +func CancellationReasonNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldCancellationReason, v)) +} + +// CancellationReasonIn applies the In predicate on the "cancellation_reason" field. +func CancellationReasonIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldCancellationReason, vs...)) +} + +// CancellationReasonNotIn applies the NotIn predicate on the "cancellation_reason" field. +func CancellationReasonNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldCancellationReason, vs...)) +} + +// CancellationReasonGT applies the GT predicate on the "cancellation_reason" field. +func CancellationReasonGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldCancellationReason, v)) +} + +// CancellationReasonGTE applies the GTE predicate on the "cancellation_reason" field. +func CancellationReasonGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldCancellationReason, v)) +} + +// CancellationReasonLT applies the LT predicate on the "cancellation_reason" field. +func CancellationReasonLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldCancellationReason, v)) +} + +// CancellationReasonLTE applies the LTE predicate on the "cancellation_reason" field. +func CancellationReasonLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldCancellationReason, v)) +} + +// CancellationReasonContains applies the Contains predicate on the "cancellation_reason" field. +func CancellationReasonContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldCancellationReason, v)) +} + +// CancellationReasonHasPrefix applies the HasPrefix predicate on the "cancellation_reason" field. +func CancellationReasonHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldCancellationReason, v)) +} + +// CancellationReasonHasSuffix applies the HasSuffix predicate on the "cancellation_reason" field. +func CancellationReasonHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldCancellationReason, v)) +} + +// CancellationReasonIsNil applies the IsNil predicate on the "cancellation_reason" field. +func CancellationReasonIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldCancellationReason)) +} + +// CancellationReasonNotNil applies the NotNil predicate on the "cancellation_reason" field. +func CancellationReasonNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldCancellationReason)) +} + +// CancellationReasonEqualFold applies the EqualFold predicate on the "cancellation_reason" field. +func CancellationReasonEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldCancellationReason, v)) +} + +// CancellationReasonContainsFold applies the ContainsFold predicate on the "cancellation_reason" field. +func CancellationReasonContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldCancellationReason, v)) +} + +// RefundReasonEQ applies the EQ predicate on the "refund_reason" field. +func RefundReasonEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldRefundReason, v)) +} + +// RefundReasonNEQ applies the NEQ predicate on the "refund_reason" field. +func RefundReasonNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldRefundReason, v)) +} + +// RefundReasonIn applies the In predicate on the "refund_reason" field. +func RefundReasonIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldRefundReason, vs...)) +} + +// RefundReasonNotIn applies the NotIn predicate on the "refund_reason" field. +func RefundReasonNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldRefundReason, vs...)) +} + +// RefundReasonGT applies the GT predicate on the "refund_reason" field. +func RefundReasonGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldRefundReason, v)) +} + +// RefundReasonGTE applies the GTE predicate on the "refund_reason" field. +func RefundReasonGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldRefundReason, v)) +} + +// RefundReasonLT applies the LT predicate on the "refund_reason" field. +func RefundReasonLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldRefundReason, v)) +} + +// RefundReasonLTE applies the LTE predicate on the "refund_reason" field. +func RefundReasonLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldRefundReason, v)) +} + +// RefundReasonContains applies the Contains predicate on the "refund_reason" field. +func RefundReasonContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldRefundReason, v)) +} + +// RefundReasonHasPrefix applies the HasPrefix predicate on the "refund_reason" field. +func RefundReasonHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldRefundReason, v)) +} + +// RefundReasonHasSuffix applies the HasSuffix predicate on the "refund_reason" field. +func RefundReasonHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldRefundReason, v)) +} + +// RefundReasonIsNil applies the IsNil predicate on the "refund_reason" field. +func RefundReasonIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldRefundReason)) +} + +// RefundReasonNotNil applies the NotNil predicate on the "refund_reason" field. +func RefundReasonNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldRefundReason)) +} + +// RefundReasonEqualFold applies the EqualFold predicate on the "refund_reason" field. +func RefundReasonEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldRefundReason, v)) +} + +// RefundReasonContainsFold applies the ContainsFold predicate on the "refund_reason" field. +func RefundReasonContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldRefundReason, v)) +} + +// IdempotencyKeyEQ applies the EQ predicate on the "idempotency_key" field. +func IdempotencyKeyEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEQ(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyNEQ applies the NEQ predicate on the "idempotency_key" field. +func IdempotencyKeyNEQ(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNEQ(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyIn applies the In predicate on the "idempotency_key" field. +func IdempotencyKeyIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIn(FieldIdempotencyKey, vs...)) +} + +// IdempotencyKeyNotIn applies the NotIn predicate on the "idempotency_key" field. +func IdempotencyKeyNotIn(vs ...string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotIn(FieldIdempotencyKey, vs...)) +} + +// IdempotencyKeyGT applies the GT predicate on the "idempotency_key" field. +func IdempotencyKeyGT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGT(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyGTE applies the GTE predicate on the "idempotency_key" field. +func IdempotencyKeyGTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldGTE(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyLT applies the LT predicate on the "idempotency_key" field. +func IdempotencyKeyLT(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLT(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyLTE applies the LTE predicate on the "idempotency_key" field. +func IdempotencyKeyLTE(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldLTE(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyContains applies the Contains predicate on the "idempotency_key" field. +func IdempotencyKeyContains(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContains(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyHasPrefix applies the HasPrefix predicate on the "idempotency_key" field. +func IdempotencyKeyHasPrefix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasPrefix(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyHasSuffix applies the HasSuffix predicate on the "idempotency_key" field. +func IdempotencyKeyHasSuffix(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldHasSuffix(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyIsNil applies the IsNil predicate on the "idempotency_key" field. +func IdempotencyKeyIsNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldIsNull(FieldIdempotencyKey)) +} + +// IdempotencyKeyNotNil applies the NotNil predicate on the "idempotency_key" field. +func IdempotencyKeyNotNil() predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldNotNull(FieldIdempotencyKey)) +} + +// IdempotencyKeyEqualFold applies the EqualFold predicate on the "idempotency_key" field. +func IdempotencyKeyEqualFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldEqualFold(FieldIdempotencyKey, v)) +} + +// IdempotencyKeyContainsFold applies the ContainsFold predicate on the "idempotency_key" field. +func IdempotencyKeyContainsFold(v string) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.FieldContainsFold(FieldIdempotencyKey, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.InternshipEnrollment) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.InternshipEnrollment) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.InternshipEnrollment) predicate.InternshipEnrollment { + return predicate.InternshipEnrollment(sql.NotPredicates(p)) +} diff --git a/ent/internshipenrollment_create.go b/ent/internshipenrollment_create.go new file mode 100644 index 0000000..45e6af6 --- /dev/null +++ b/ent/internshipenrollment_create.go @@ -0,0 +1,548 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/internshipenrollment" + "github.com/omkar273/codegeeky/internal/types" +) + +// InternshipEnrollmentCreate is the builder for creating a InternshipEnrollment entity. +type InternshipEnrollmentCreate struct { + config + mutation *InternshipEnrollmentMutation + hooks []Hook +} + +// SetStatus sets the "status" field. +func (iec *InternshipEnrollmentCreate) SetStatus(s string) *InternshipEnrollmentCreate { + iec.mutation.SetStatus(s) + return iec +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableStatus(s *string) *InternshipEnrollmentCreate { + if s != nil { + iec.SetStatus(*s) + } + return iec +} + +// SetCreatedAt sets the "created_at" field. +func (iec *InternshipEnrollmentCreate) SetCreatedAt(t time.Time) *InternshipEnrollmentCreate { + iec.mutation.SetCreatedAt(t) + return iec +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableCreatedAt(t *time.Time) *InternshipEnrollmentCreate { + if t != nil { + iec.SetCreatedAt(*t) + } + return iec +} + +// SetUpdatedAt sets the "updated_at" field. +func (iec *InternshipEnrollmentCreate) SetUpdatedAt(t time.Time) *InternshipEnrollmentCreate { + iec.mutation.SetUpdatedAt(t) + return iec +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableUpdatedAt(t *time.Time) *InternshipEnrollmentCreate { + if t != nil { + iec.SetUpdatedAt(*t) + } + return iec +} + +// SetCreatedBy sets the "created_by" field. +func (iec *InternshipEnrollmentCreate) SetCreatedBy(s string) *InternshipEnrollmentCreate { + iec.mutation.SetCreatedBy(s) + return iec +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableCreatedBy(s *string) *InternshipEnrollmentCreate { + if s != nil { + iec.SetCreatedBy(*s) + } + return iec +} + +// SetUpdatedBy sets the "updated_by" field. +func (iec *InternshipEnrollmentCreate) SetUpdatedBy(s string) *InternshipEnrollmentCreate { + iec.mutation.SetUpdatedBy(s) + return iec +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableUpdatedBy(s *string) *InternshipEnrollmentCreate { + if s != nil { + iec.SetUpdatedBy(*s) + } + return iec +} + +// SetMetadata sets the "metadata" field. +func (iec *InternshipEnrollmentCreate) SetMetadata(m map[string]string) *InternshipEnrollmentCreate { + iec.mutation.SetMetadata(m) + return iec +} + +// SetUserID sets the "user_id" field. +func (iec *InternshipEnrollmentCreate) SetUserID(s string) *InternshipEnrollmentCreate { + iec.mutation.SetUserID(s) + return iec +} + +// SetInternshipID sets the "internship_id" field. +func (iec *InternshipEnrollmentCreate) SetInternshipID(s string) *InternshipEnrollmentCreate { + iec.mutation.SetInternshipID(s) + return iec +} + +// SetInternshipBatchID sets the "internship_batch_id" field. +func (iec *InternshipEnrollmentCreate) SetInternshipBatchID(s string) *InternshipEnrollmentCreate { + iec.mutation.SetInternshipBatchID(s) + return iec +} + +// SetEnrollmentStatus sets the "enrollment_status" field. +func (iec *InternshipEnrollmentCreate) SetEnrollmentStatus(tes types.InternshipEnrollmentStatus) *InternshipEnrollmentCreate { + iec.mutation.SetEnrollmentStatus(tes) + return iec +} + +// SetNillableEnrollmentStatus sets the "enrollment_status" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableEnrollmentStatus(tes *types.InternshipEnrollmentStatus) *InternshipEnrollmentCreate { + if tes != nil { + iec.SetEnrollmentStatus(*tes) + } + return iec +} + +// SetPaymentStatus sets the "payment_status" field. +func (iec *InternshipEnrollmentCreate) SetPaymentStatus(ts types.PaymentStatus) *InternshipEnrollmentCreate { + iec.mutation.SetPaymentStatus(ts) + return iec +} + +// SetNillablePaymentStatus sets the "payment_status" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillablePaymentStatus(ts *types.PaymentStatus) *InternshipEnrollmentCreate { + if ts != nil { + iec.SetPaymentStatus(*ts) + } + return iec +} + +// SetEnrolledAt sets the "enrolled_at" field. +func (iec *InternshipEnrollmentCreate) SetEnrolledAt(t time.Time) *InternshipEnrollmentCreate { + iec.mutation.SetEnrolledAt(t) + return iec +} + +// SetNillableEnrolledAt sets the "enrolled_at" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableEnrolledAt(t *time.Time) *InternshipEnrollmentCreate { + if t != nil { + iec.SetEnrolledAt(*t) + } + return iec +} + +// SetPaymentID sets the "payment_id" field. +func (iec *InternshipEnrollmentCreate) SetPaymentID(s string) *InternshipEnrollmentCreate { + iec.mutation.SetPaymentID(s) + return iec +} + +// SetNillablePaymentID sets the "payment_id" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillablePaymentID(s *string) *InternshipEnrollmentCreate { + if s != nil { + iec.SetPaymentID(*s) + } + return iec +} + +// SetRefundedAt sets the "refunded_at" field. +func (iec *InternshipEnrollmentCreate) SetRefundedAt(t time.Time) *InternshipEnrollmentCreate { + iec.mutation.SetRefundedAt(t) + return iec +} + +// SetNillableRefundedAt sets the "refunded_at" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableRefundedAt(t *time.Time) *InternshipEnrollmentCreate { + if t != nil { + iec.SetRefundedAt(*t) + } + return iec +} + +// SetCancellationReason sets the "cancellation_reason" field. +func (iec *InternshipEnrollmentCreate) SetCancellationReason(s string) *InternshipEnrollmentCreate { + iec.mutation.SetCancellationReason(s) + return iec +} + +// SetNillableCancellationReason sets the "cancellation_reason" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableCancellationReason(s *string) *InternshipEnrollmentCreate { + if s != nil { + iec.SetCancellationReason(*s) + } + return iec +} + +// SetRefundReason sets the "refund_reason" field. +func (iec *InternshipEnrollmentCreate) SetRefundReason(s string) *InternshipEnrollmentCreate { + iec.mutation.SetRefundReason(s) + return iec +} + +// SetNillableRefundReason sets the "refund_reason" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableRefundReason(s *string) *InternshipEnrollmentCreate { + if s != nil { + iec.SetRefundReason(*s) + } + return iec +} + +// SetIdempotencyKey sets the "idempotency_key" field. +func (iec *InternshipEnrollmentCreate) SetIdempotencyKey(s string) *InternshipEnrollmentCreate { + iec.mutation.SetIdempotencyKey(s) + return iec +} + +// SetNillableIdempotencyKey sets the "idempotency_key" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableIdempotencyKey(s *string) *InternshipEnrollmentCreate { + if s != nil { + iec.SetIdempotencyKey(*s) + } + return iec +} + +// SetID sets the "id" field. +func (iec *InternshipEnrollmentCreate) SetID(s string) *InternshipEnrollmentCreate { + iec.mutation.SetID(s) + return iec +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (iec *InternshipEnrollmentCreate) SetNillableID(s *string) *InternshipEnrollmentCreate { + if s != nil { + iec.SetID(*s) + } + return iec +} + +// Mutation returns the InternshipEnrollmentMutation object of the builder. +func (iec *InternshipEnrollmentCreate) Mutation() *InternshipEnrollmentMutation { + return iec.mutation +} + +// Save creates the InternshipEnrollment in the database. +func (iec *InternshipEnrollmentCreate) Save(ctx context.Context) (*InternshipEnrollment, error) { + iec.defaults() + return withHooks(ctx, iec.sqlSave, iec.mutation, iec.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (iec *InternshipEnrollmentCreate) SaveX(ctx context.Context) *InternshipEnrollment { + v, err := iec.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (iec *InternshipEnrollmentCreate) Exec(ctx context.Context) error { + _, err := iec.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (iec *InternshipEnrollmentCreate) ExecX(ctx context.Context) { + if err := iec.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (iec *InternshipEnrollmentCreate) defaults() { + if _, ok := iec.mutation.Status(); !ok { + v := internshipenrollment.DefaultStatus + iec.mutation.SetStatus(v) + } + if _, ok := iec.mutation.CreatedAt(); !ok { + v := internshipenrollment.DefaultCreatedAt() + iec.mutation.SetCreatedAt(v) + } + if _, ok := iec.mutation.UpdatedAt(); !ok { + v := internshipenrollment.DefaultUpdatedAt() + iec.mutation.SetUpdatedAt(v) + } + if _, ok := iec.mutation.Metadata(); !ok { + v := internshipenrollment.DefaultMetadata + iec.mutation.SetMetadata(v) + } + if _, ok := iec.mutation.EnrollmentStatus(); !ok { + v := internshipenrollment.DefaultEnrollmentStatus + iec.mutation.SetEnrollmentStatus(v) + } + if _, ok := iec.mutation.PaymentStatus(); !ok { + v := internshipenrollment.DefaultPaymentStatus + iec.mutation.SetPaymentStatus(v) + } + if _, ok := iec.mutation.ID(); !ok { + v := internshipenrollment.DefaultID() + iec.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (iec *InternshipEnrollmentCreate) check() error { + if _, ok := iec.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "InternshipEnrollment.status"`)} + } + if _, ok := iec.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "InternshipEnrollment.created_at"`)} + } + if _, ok := iec.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "InternshipEnrollment.updated_at"`)} + } + if _, ok := iec.mutation.UserID(); !ok { + return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "InternshipEnrollment.user_id"`)} + } + if v, ok := iec.mutation.UserID(); ok { + if err := internshipenrollment.UserIDValidator(v); err != nil { + return &ValidationError{Name: "user_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.user_id": %w`, err)} + } + } + if _, ok := iec.mutation.InternshipID(); !ok { + return &ValidationError{Name: "internship_id", err: errors.New(`ent: missing required field "InternshipEnrollment.internship_id"`)} + } + if v, ok := iec.mutation.InternshipID(); ok { + if err := internshipenrollment.InternshipIDValidator(v); err != nil { + return &ValidationError{Name: "internship_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.internship_id": %w`, err)} + } + } + if _, ok := iec.mutation.InternshipBatchID(); !ok { + return &ValidationError{Name: "internship_batch_id", err: errors.New(`ent: missing required field "InternshipEnrollment.internship_batch_id"`)} + } + if v, ok := iec.mutation.InternshipBatchID(); ok { + if err := internshipenrollment.InternshipBatchIDValidator(v); err != nil { + return &ValidationError{Name: "internship_batch_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.internship_batch_id": %w`, err)} + } + } + if _, ok := iec.mutation.EnrollmentStatus(); !ok { + return &ValidationError{Name: "enrollment_status", err: errors.New(`ent: missing required field "InternshipEnrollment.enrollment_status"`)} + } + if v, ok := iec.mutation.EnrollmentStatus(); ok { + if err := internshipenrollment.EnrollmentStatusValidator(string(v)); err != nil { + return &ValidationError{Name: "enrollment_status", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.enrollment_status": %w`, err)} + } + } + if _, ok := iec.mutation.PaymentStatus(); !ok { + return &ValidationError{Name: "payment_status", err: errors.New(`ent: missing required field "InternshipEnrollment.payment_status"`)} + } + if v, ok := iec.mutation.PaymentStatus(); ok { + if err := internshipenrollment.PaymentStatusValidator(string(v)); err != nil { + return &ValidationError{Name: "payment_status", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.payment_status": %w`, err)} + } + } + return nil +} + +func (iec *InternshipEnrollmentCreate) sqlSave(ctx context.Context) (*InternshipEnrollment, error) { + if err := iec.check(); err != nil { + return nil, err + } + _node, _spec := iec.createSpec() + if err := sqlgraph.CreateNode(ctx, iec.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(string); ok { + _node.ID = id + } else { + return nil, fmt.Errorf("unexpected InternshipEnrollment.ID type: %T", _spec.ID.Value) + } + } + iec.mutation.id = &_node.ID + iec.mutation.done = true + return _node, nil +} + +func (iec *InternshipEnrollmentCreate) createSpec() (*InternshipEnrollment, *sqlgraph.CreateSpec) { + var ( + _node = &InternshipEnrollment{config: iec.config} + _spec = sqlgraph.NewCreateSpec(internshipenrollment.Table, sqlgraph.NewFieldSpec(internshipenrollment.FieldID, field.TypeString)) + ) + if id, ok := iec.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := iec.mutation.Status(); ok { + _spec.SetField(internshipenrollment.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := iec.mutation.CreatedAt(); ok { + _spec.SetField(internshipenrollment.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := iec.mutation.UpdatedAt(); ok { + _spec.SetField(internshipenrollment.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := iec.mutation.CreatedBy(); ok { + _spec.SetField(internshipenrollment.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := iec.mutation.UpdatedBy(); ok { + _spec.SetField(internshipenrollment.FieldUpdatedBy, field.TypeString, value) + _node.UpdatedBy = value + } + if value, ok := iec.mutation.Metadata(); ok { + _spec.SetField(internshipenrollment.FieldMetadata, field.TypeJSON, value) + _node.Metadata = value + } + if value, ok := iec.mutation.UserID(); ok { + _spec.SetField(internshipenrollment.FieldUserID, field.TypeString, value) + _node.UserID = value + } + if value, ok := iec.mutation.InternshipID(); ok { + _spec.SetField(internshipenrollment.FieldInternshipID, field.TypeString, value) + _node.InternshipID = value + } + if value, ok := iec.mutation.InternshipBatchID(); ok { + _spec.SetField(internshipenrollment.FieldInternshipBatchID, field.TypeString, value) + _node.InternshipBatchID = value + } + if value, ok := iec.mutation.EnrollmentStatus(); ok { + _spec.SetField(internshipenrollment.FieldEnrollmentStatus, field.TypeString, value) + _node.EnrollmentStatus = value + } + if value, ok := iec.mutation.PaymentStatus(); ok { + _spec.SetField(internshipenrollment.FieldPaymentStatus, field.TypeString, value) + _node.PaymentStatus = value + } + if value, ok := iec.mutation.EnrolledAt(); ok { + _spec.SetField(internshipenrollment.FieldEnrolledAt, field.TypeTime, value) + _node.EnrolledAt = &value + } + if value, ok := iec.mutation.PaymentID(); ok { + _spec.SetField(internshipenrollment.FieldPaymentID, field.TypeString, value) + _node.PaymentID = &value + } + if value, ok := iec.mutation.RefundedAt(); ok { + _spec.SetField(internshipenrollment.FieldRefundedAt, field.TypeTime, value) + _node.RefundedAt = &value + } + if value, ok := iec.mutation.CancellationReason(); ok { + _spec.SetField(internshipenrollment.FieldCancellationReason, field.TypeString, value) + _node.CancellationReason = &value + } + if value, ok := iec.mutation.RefundReason(); ok { + _spec.SetField(internshipenrollment.FieldRefundReason, field.TypeString, value) + _node.RefundReason = &value + } + if value, ok := iec.mutation.IdempotencyKey(); ok { + _spec.SetField(internshipenrollment.FieldIdempotencyKey, field.TypeString, value) + _node.IdempotencyKey = &value + } + return _node, _spec +} + +// InternshipEnrollmentCreateBulk is the builder for creating many InternshipEnrollment entities in bulk. +type InternshipEnrollmentCreateBulk struct { + config + err error + builders []*InternshipEnrollmentCreate +} + +// Save creates the InternshipEnrollment entities in the database. +func (iecb *InternshipEnrollmentCreateBulk) Save(ctx context.Context) ([]*InternshipEnrollment, error) { + if iecb.err != nil { + return nil, iecb.err + } + specs := make([]*sqlgraph.CreateSpec, len(iecb.builders)) + nodes := make([]*InternshipEnrollment, len(iecb.builders)) + mutators := make([]Mutator, len(iecb.builders)) + for i := range iecb.builders { + func(i int, root context.Context) { + builder := iecb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*InternshipEnrollmentMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, iecb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, iecb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, iecb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (iecb *InternshipEnrollmentCreateBulk) SaveX(ctx context.Context) []*InternshipEnrollment { + v, err := iecb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (iecb *InternshipEnrollmentCreateBulk) Exec(ctx context.Context) error { + _, err := iecb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (iecb *InternshipEnrollmentCreateBulk) ExecX(ctx context.Context) { + if err := iecb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/internshipenrollment_delete.go b/ent/internshipenrollment_delete.go new file mode 100644 index 0000000..037cc01 --- /dev/null +++ b/ent/internshipenrollment_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/internshipenrollment" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// InternshipEnrollmentDelete is the builder for deleting a InternshipEnrollment entity. +type InternshipEnrollmentDelete struct { + config + hooks []Hook + mutation *InternshipEnrollmentMutation +} + +// Where appends a list predicates to the InternshipEnrollmentDelete builder. +func (ied *InternshipEnrollmentDelete) Where(ps ...predicate.InternshipEnrollment) *InternshipEnrollmentDelete { + ied.mutation.Where(ps...) + return ied +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (ied *InternshipEnrollmentDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, ied.sqlExec, ied.mutation, ied.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (ied *InternshipEnrollmentDelete) ExecX(ctx context.Context) int { + n, err := ied.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (ied *InternshipEnrollmentDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(internshipenrollment.Table, sqlgraph.NewFieldSpec(internshipenrollment.FieldID, field.TypeString)) + if ps := ied.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, ied.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + ied.mutation.done = true + return affected, err +} + +// InternshipEnrollmentDeleteOne is the builder for deleting a single InternshipEnrollment entity. +type InternshipEnrollmentDeleteOne struct { + ied *InternshipEnrollmentDelete +} + +// Where appends a list predicates to the InternshipEnrollmentDelete builder. +func (iedo *InternshipEnrollmentDeleteOne) Where(ps ...predicate.InternshipEnrollment) *InternshipEnrollmentDeleteOne { + iedo.ied.mutation.Where(ps...) + return iedo +} + +// Exec executes the deletion query. +func (iedo *InternshipEnrollmentDeleteOne) Exec(ctx context.Context) error { + n, err := iedo.ied.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{internshipenrollment.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (iedo *InternshipEnrollmentDeleteOne) ExecX(ctx context.Context) { + if err := iedo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/internshipenrollment_query.go b/ent/internshipenrollment_query.go new file mode 100644 index 0000000..5c2d319 --- /dev/null +++ b/ent/internshipenrollment_query.go @@ -0,0 +1,527 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/internshipenrollment" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// InternshipEnrollmentQuery is the builder for querying InternshipEnrollment entities. +type InternshipEnrollmentQuery struct { + config + ctx *QueryContext + order []internshipenrollment.OrderOption + inters []Interceptor + predicates []predicate.InternshipEnrollment + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the InternshipEnrollmentQuery builder. +func (ieq *InternshipEnrollmentQuery) Where(ps ...predicate.InternshipEnrollment) *InternshipEnrollmentQuery { + ieq.predicates = append(ieq.predicates, ps...) + return ieq +} + +// Limit the number of records to be returned by this query. +func (ieq *InternshipEnrollmentQuery) Limit(limit int) *InternshipEnrollmentQuery { + ieq.ctx.Limit = &limit + return ieq +} + +// Offset to start from. +func (ieq *InternshipEnrollmentQuery) Offset(offset int) *InternshipEnrollmentQuery { + ieq.ctx.Offset = &offset + return ieq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (ieq *InternshipEnrollmentQuery) Unique(unique bool) *InternshipEnrollmentQuery { + ieq.ctx.Unique = &unique + return ieq +} + +// Order specifies how the records should be ordered. +func (ieq *InternshipEnrollmentQuery) Order(o ...internshipenrollment.OrderOption) *InternshipEnrollmentQuery { + ieq.order = append(ieq.order, o...) + return ieq +} + +// First returns the first InternshipEnrollment entity from the query. +// Returns a *NotFoundError when no InternshipEnrollment was found. +func (ieq *InternshipEnrollmentQuery) First(ctx context.Context) (*InternshipEnrollment, error) { + nodes, err := ieq.Limit(1).All(setContextOp(ctx, ieq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{internshipenrollment.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (ieq *InternshipEnrollmentQuery) FirstX(ctx context.Context) *InternshipEnrollment { + node, err := ieq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first InternshipEnrollment ID from the query. +// Returns a *NotFoundError when no InternshipEnrollment ID was found. +func (ieq *InternshipEnrollmentQuery) FirstID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = ieq.Limit(1).IDs(setContextOp(ctx, ieq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{internshipenrollment.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (ieq *InternshipEnrollmentQuery) FirstIDX(ctx context.Context) string { + id, err := ieq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single InternshipEnrollment entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one InternshipEnrollment entity is found. +// Returns a *NotFoundError when no InternshipEnrollment entities are found. +func (ieq *InternshipEnrollmentQuery) Only(ctx context.Context) (*InternshipEnrollment, error) { + nodes, err := ieq.Limit(2).All(setContextOp(ctx, ieq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{internshipenrollment.Label} + default: + return nil, &NotSingularError{internshipenrollment.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (ieq *InternshipEnrollmentQuery) OnlyX(ctx context.Context) *InternshipEnrollment { + node, err := ieq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only InternshipEnrollment ID in the query. +// Returns a *NotSingularError when more than one InternshipEnrollment ID is found. +// Returns a *NotFoundError when no entities are found. +func (ieq *InternshipEnrollmentQuery) OnlyID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = ieq.Limit(2).IDs(setContextOp(ctx, ieq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{internshipenrollment.Label} + default: + err = &NotSingularError{internshipenrollment.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (ieq *InternshipEnrollmentQuery) OnlyIDX(ctx context.Context) string { + id, err := ieq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of InternshipEnrollments. +func (ieq *InternshipEnrollmentQuery) All(ctx context.Context) ([]*InternshipEnrollment, error) { + ctx = setContextOp(ctx, ieq.ctx, ent.OpQueryAll) + if err := ieq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*InternshipEnrollment, *InternshipEnrollmentQuery]() + return withInterceptors[[]*InternshipEnrollment](ctx, ieq, qr, ieq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (ieq *InternshipEnrollmentQuery) AllX(ctx context.Context) []*InternshipEnrollment { + nodes, err := ieq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of InternshipEnrollment IDs. +func (ieq *InternshipEnrollmentQuery) IDs(ctx context.Context) (ids []string, err error) { + if ieq.ctx.Unique == nil && ieq.path != nil { + ieq.Unique(true) + } + ctx = setContextOp(ctx, ieq.ctx, ent.OpQueryIDs) + if err = ieq.Select(internshipenrollment.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (ieq *InternshipEnrollmentQuery) IDsX(ctx context.Context) []string { + ids, err := ieq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (ieq *InternshipEnrollmentQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, ieq.ctx, ent.OpQueryCount) + if err := ieq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, ieq, querierCount[*InternshipEnrollmentQuery](), ieq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (ieq *InternshipEnrollmentQuery) CountX(ctx context.Context) int { + count, err := ieq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (ieq *InternshipEnrollmentQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, ieq.ctx, ent.OpQueryExist) + switch _, err := ieq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (ieq *InternshipEnrollmentQuery) ExistX(ctx context.Context) bool { + exist, err := ieq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the InternshipEnrollmentQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (ieq *InternshipEnrollmentQuery) Clone() *InternshipEnrollmentQuery { + if ieq == nil { + return nil + } + return &InternshipEnrollmentQuery{ + config: ieq.config, + ctx: ieq.ctx.Clone(), + order: append([]internshipenrollment.OrderOption{}, ieq.order...), + inters: append([]Interceptor{}, ieq.inters...), + predicates: append([]predicate.InternshipEnrollment{}, ieq.predicates...), + // clone intermediate query. + sql: ieq.sql.Clone(), + path: ieq.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Status string `json:"status,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.InternshipEnrollment.Query(). +// GroupBy(internshipenrollment.FieldStatus). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (ieq *InternshipEnrollmentQuery) GroupBy(field string, fields ...string) *InternshipEnrollmentGroupBy { + ieq.ctx.Fields = append([]string{field}, fields...) + grbuild := &InternshipEnrollmentGroupBy{build: ieq} + grbuild.flds = &ieq.ctx.Fields + grbuild.label = internshipenrollment.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Status string `json:"status,omitempty"` +// } +// +// client.InternshipEnrollment.Query(). +// Select(internshipenrollment.FieldStatus). +// Scan(ctx, &v) +func (ieq *InternshipEnrollmentQuery) Select(fields ...string) *InternshipEnrollmentSelect { + ieq.ctx.Fields = append(ieq.ctx.Fields, fields...) + sbuild := &InternshipEnrollmentSelect{InternshipEnrollmentQuery: ieq} + sbuild.label = internshipenrollment.Label + sbuild.flds, sbuild.scan = &ieq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a InternshipEnrollmentSelect configured with the given aggregations. +func (ieq *InternshipEnrollmentQuery) Aggregate(fns ...AggregateFunc) *InternshipEnrollmentSelect { + return ieq.Select().Aggregate(fns...) +} + +func (ieq *InternshipEnrollmentQuery) prepareQuery(ctx context.Context) error { + for _, inter := range ieq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, ieq); err != nil { + return err + } + } + } + for _, f := range ieq.ctx.Fields { + if !internshipenrollment.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if ieq.path != nil { + prev, err := ieq.path(ctx) + if err != nil { + return err + } + ieq.sql = prev + } + return nil +} + +func (ieq *InternshipEnrollmentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*InternshipEnrollment, error) { + var ( + nodes = []*InternshipEnrollment{} + _spec = ieq.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*InternshipEnrollment).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &InternshipEnrollment{config: ieq.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, ieq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (ieq *InternshipEnrollmentQuery) sqlCount(ctx context.Context) (int, error) { + _spec := ieq.querySpec() + _spec.Node.Columns = ieq.ctx.Fields + if len(ieq.ctx.Fields) > 0 { + _spec.Unique = ieq.ctx.Unique != nil && *ieq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, ieq.driver, _spec) +} + +func (ieq *InternshipEnrollmentQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(internshipenrollment.Table, internshipenrollment.Columns, sqlgraph.NewFieldSpec(internshipenrollment.FieldID, field.TypeString)) + _spec.From = ieq.sql + if unique := ieq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if ieq.path != nil { + _spec.Unique = true + } + if fields := ieq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, internshipenrollment.FieldID) + for i := range fields { + if fields[i] != internshipenrollment.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := ieq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := ieq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := ieq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := ieq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (ieq *InternshipEnrollmentQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(ieq.driver.Dialect()) + t1 := builder.Table(internshipenrollment.Table) + columns := ieq.ctx.Fields + if len(columns) == 0 { + columns = internshipenrollment.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if ieq.sql != nil { + selector = ieq.sql + selector.Select(selector.Columns(columns...)...) + } + if ieq.ctx.Unique != nil && *ieq.ctx.Unique { + selector.Distinct() + } + for _, p := range ieq.predicates { + p(selector) + } + for _, p := range ieq.order { + p(selector) + } + if offset := ieq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := ieq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// InternshipEnrollmentGroupBy is the group-by builder for InternshipEnrollment entities. +type InternshipEnrollmentGroupBy struct { + selector + build *InternshipEnrollmentQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (iegb *InternshipEnrollmentGroupBy) Aggregate(fns ...AggregateFunc) *InternshipEnrollmentGroupBy { + iegb.fns = append(iegb.fns, fns...) + return iegb +} + +// Scan applies the selector query and scans the result into the given value. +func (iegb *InternshipEnrollmentGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, iegb.build.ctx, ent.OpQueryGroupBy) + if err := iegb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*InternshipEnrollmentQuery, *InternshipEnrollmentGroupBy](ctx, iegb.build, iegb, iegb.build.inters, v) +} + +func (iegb *InternshipEnrollmentGroupBy) sqlScan(ctx context.Context, root *InternshipEnrollmentQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(iegb.fns)) + for _, fn := range iegb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*iegb.flds)+len(iegb.fns)) + for _, f := range *iegb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*iegb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := iegb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// InternshipEnrollmentSelect is the builder for selecting fields of InternshipEnrollment entities. +type InternshipEnrollmentSelect struct { + *InternshipEnrollmentQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (ies *InternshipEnrollmentSelect) Aggregate(fns ...AggregateFunc) *InternshipEnrollmentSelect { + ies.fns = append(ies.fns, fns...) + return ies +} + +// Scan applies the selector query and scans the result into the given value. +func (ies *InternshipEnrollmentSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, ies.ctx, ent.OpQuerySelect) + if err := ies.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*InternshipEnrollmentQuery, *InternshipEnrollmentSelect](ctx, ies.InternshipEnrollmentQuery, ies, ies.inters, v) +} + +func (ies *InternshipEnrollmentSelect) sqlScan(ctx context.Context, root *InternshipEnrollmentQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(ies.fns)) + for _, fn := range ies.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*ies.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := ies.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/internshipenrollment_update.go b/ent/internshipenrollment_update.go new file mode 100644 index 0000000..20a6808 --- /dev/null +++ b/ent/internshipenrollment_update.go @@ -0,0 +1,889 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/internshipenrollment" + "github.com/omkar273/codegeeky/ent/predicate" + "github.com/omkar273/codegeeky/internal/types" +) + +// InternshipEnrollmentUpdate is the builder for updating InternshipEnrollment entities. +type InternshipEnrollmentUpdate struct { + config + hooks []Hook + mutation *InternshipEnrollmentMutation +} + +// Where appends a list predicates to the InternshipEnrollmentUpdate builder. +func (ieu *InternshipEnrollmentUpdate) Where(ps ...predicate.InternshipEnrollment) *InternshipEnrollmentUpdate { + ieu.mutation.Where(ps...) + return ieu +} + +// SetStatus sets the "status" field. +func (ieu *InternshipEnrollmentUpdate) SetStatus(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetStatus(s) + return ieu +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableStatus(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetStatus(*s) + } + return ieu +} + +// SetUpdatedAt sets the "updated_at" field. +func (ieu *InternshipEnrollmentUpdate) SetUpdatedAt(t time.Time) *InternshipEnrollmentUpdate { + ieu.mutation.SetUpdatedAt(t) + return ieu +} + +// SetUpdatedBy sets the "updated_by" field. +func (ieu *InternshipEnrollmentUpdate) SetUpdatedBy(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetUpdatedBy(s) + return ieu +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableUpdatedBy(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetUpdatedBy(*s) + } + return ieu +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (ieu *InternshipEnrollmentUpdate) ClearUpdatedBy() *InternshipEnrollmentUpdate { + ieu.mutation.ClearUpdatedBy() + return ieu +} + +// SetMetadata sets the "metadata" field. +func (ieu *InternshipEnrollmentUpdate) SetMetadata(m map[string]string) *InternshipEnrollmentUpdate { + ieu.mutation.SetMetadata(m) + return ieu +} + +// ClearMetadata clears the value of the "metadata" field. +func (ieu *InternshipEnrollmentUpdate) ClearMetadata() *InternshipEnrollmentUpdate { + ieu.mutation.ClearMetadata() + return ieu +} + +// SetUserID sets the "user_id" field. +func (ieu *InternshipEnrollmentUpdate) SetUserID(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetUserID(s) + return ieu +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableUserID(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetUserID(*s) + } + return ieu +} + +// SetInternshipID sets the "internship_id" field. +func (ieu *InternshipEnrollmentUpdate) SetInternshipID(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetInternshipID(s) + return ieu +} + +// SetNillableInternshipID sets the "internship_id" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableInternshipID(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetInternshipID(*s) + } + return ieu +} + +// SetInternshipBatchID sets the "internship_batch_id" field. +func (ieu *InternshipEnrollmentUpdate) SetInternshipBatchID(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetInternshipBatchID(s) + return ieu +} + +// SetNillableInternshipBatchID sets the "internship_batch_id" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableInternshipBatchID(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetInternshipBatchID(*s) + } + return ieu +} + +// SetEnrollmentStatus sets the "enrollment_status" field. +func (ieu *InternshipEnrollmentUpdate) SetEnrollmentStatus(tes types.InternshipEnrollmentStatus) *InternshipEnrollmentUpdate { + ieu.mutation.SetEnrollmentStatus(tes) + return ieu +} + +// SetNillableEnrollmentStatus sets the "enrollment_status" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableEnrollmentStatus(tes *types.InternshipEnrollmentStatus) *InternshipEnrollmentUpdate { + if tes != nil { + ieu.SetEnrollmentStatus(*tes) + } + return ieu +} + +// SetPaymentStatus sets the "payment_status" field. +func (ieu *InternshipEnrollmentUpdate) SetPaymentStatus(ts types.PaymentStatus) *InternshipEnrollmentUpdate { + ieu.mutation.SetPaymentStatus(ts) + return ieu +} + +// SetNillablePaymentStatus sets the "payment_status" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillablePaymentStatus(ts *types.PaymentStatus) *InternshipEnrollmentUpdate { + if ts != nil { + ieu.SetPaymentStatus(*ts) + } + return ieu +} + +// SetEnrolledAt sets the "enrolled_at" field. +func (ieu *InternshipEnrollmentUpdate) SetEnrolledAt(t time.Time) *InternshipEnrollmentUpdate { + ieu.mutation.SetEnrolledAt(t) + return ieu +} + +// SetNillableEnrolledAt sets the "enrolled_at" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableEnrolledAt(t *time.Time) *InternshipEnrollmentUpdate { + if t != nil { + ieu.SetEnrolledAt(*t) + } + return ieu +} + +// ClearEnrolledAt clears the value of the "enrolled_at" field. +func (ieu *InternshipEnrollmentUpdate) ClearEnrolledAt() *InternshipEnrollmentUpdate { + ieu.mutation.ClearEnrolledAt() + return ieu +} + +// SetPaymentID sets the "payment_id" field. +func (ieu *InternshipEnrollmentUpdate) SetPaymentID(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetPaymentID(s) + return ieu +} + +// SetNillablePaymentID sets the "payment_id" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillablePaymentID(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetPaymentID(*s) + } + return ieu +} + +// ClearPaymentID clears the value of the "payment_id" field. +func (ieu *InternshipEnrollmentUpdate) ClearPaymentID() *InternshipEnrollmentUpdate { + ieu.mutation.ClearPaymentID() + return ieu +} + +// SetRefundedAt sets the "refunded_at" field. +func (ieu *InternshipEnrollmentUpdate) SetRefundedAt(t time.Time) *InternshipEnrollmentUpdate { + ieu.mutation.SetRefundedAt(t) + return ieu +} + +// SetNillableRefundedAt sets the "refunded_at" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableRefundedAt(t *time.Time) *InternshipEnrollmentUpdate { + if t != nil { + ieu.SetRefundedAt(*t) + } + return ieu +} + +// ClearRefundedAt clears the value of the "refunded_at" field. +func (ieu *InternshipEnrollmentUpdate) ClearRefundedAt() *InternshipEnrollmentUpdate { + ieu.mutation.ClearRefundedAt() + return ieu +} + +// SetCancellationReason sets the "cancellation_reason" field. +func (ieu *InternshipEnrollmentUpdate) SetCancellationReason(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetCancellationReason(s) + return ieu +} + +// SetNillableCancellationReason sets the "cancellation_reason" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableCancellationReason(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetCancellationReason(*s) + } + return ieu +} + +// ClearCancellationReason clears the value of the "cancellation_reason" field. +func (ieu *InternshipEnrollmentUpdate) ClearCancellationReason() *InternshipEnrollmentUpdate { + ieu.mutation.ClearCancellationReason() + return ieu +} + +// SetRefundReason sets the "refund_reason" field. +func (ieu *InternshipEnrollmentUpdate) SetRefundReason(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetRefundReason(s) + return ieu +} + +// SetNillableRefundReason sets the "refund_reason" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableRefundReason(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetRefundReason(*s) + } + return ieu +} + +// ClearRefundReason clears the value of the "refund_reason" field. +func (ieu *InternshipEnrollmentUpdate) ClearRefundReason() *InternshipEnrollmentUpdate { + ieu.mutation.ClearRefundReason() + return ieu +} + +// SetIdempotencyKey sets the "idempotency_key" field. +func (ieu *InternshipEnrollmentUpdate) SetIdempotencyKey(s string) *InternshipEnrollmentUpdate { + ieu.mutation.SetIdempotencyKey(s) + return ieu +} + +// SetNillableIdempotencyKey sets the "idempotency_key" field if the given value is not nil. +func (ieu *InternshipEnrollmentUpdate) SetNillableIdempotencyKey(s *string) *InternshipEnrollmentUpdate { + if s != nil { + ieu.SetIdempotencyKey(*s) + } + return ieu +} + +// ClearIdempotencyKey clears the value of the "idempotency_key" field. +func (ieu *InternshipEnrollmentUpdate) ClearIdempotencyKey() *InternshipEnrollmentUpdate { + ieu.mutation.ClearIdempotencyKey() + return ieu +} + +// Mutation returns the InternshipEnrollmentMutation object of the builder. +func (ieu *InternshipEnrollmentUpdate) Mutation() *InternshipEnrollmentMutation { + return ieu.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (ieu *InternshipEnrollmentUpdate) Save(ctx context.Context) (int, error) { + ieu.defaults() + return withHooks(ctx, ieu.sqlSave, ieu.mutation, ieu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ieu *InternshipEnrollmentUpdate) SaveX(ctx context.Context) int { + affected, err := ieu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (ieu *InternshipEnrollmentUpdate) Exec(ctx context.Context) error { + _, err := ieu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ieu *InternshipEnrollmentUpdate) ExecX(ctx context.Context) { + if err := ieu.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (ieu *InternshipEnrollmentUpdate) defaults() { + if _, ok := ieu.mutation.UpdatedAt(); !ok { + v := internshipenrollment.UpdateDefaultUpdatedAt() + ieu.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (ieu *InternshipEnrollmentUpdate) check() error { + if v, ok := ieu.mutation.UserID(); ok { + if err := internshipenrollment.UserIDValidator(v); err != nil { + return &ValidationError{Name: "user_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.user_id": %w`, err)} + } + } + if v, ok := ieu.mutation.InternshipID(); ok { + if err := internshipenrollment.InternshipIDValidator(v); err != nil { + return &ValidationError{Name: "internship_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.internship_id": %w`, err)} + } + } + if v, ok := ieu.mutation.InternshipBatchID(); ok { + if err := internshipenrollment.InternshipBatchIDValidator(v); err != nil { + return &ValidationError{Name: "internship_batch_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.internship_batch_id": %w`, err)} + } + } + if v, ok := ieu.mutation.EnrollmentStatus(); ok { + if err := internshipenrollment.EnrollmentStatusValidator(string(v)); err != nil { + return &ValidationError{Name: "enrollment_status", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.enrollment_status": %w`, err)} + } + } + if v, ok := ieu.mutation.PaymentStatus(); ok { + if err := internshipenrollment.PaymentStatusValidator(string(v)); err != nil { + return &ValidationError{Name: "payment_status", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.payment_status": %w`, err)} + } + } + return nil +} + +func (ieu *InternshipEnrollmentUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := ieu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(internshipenrollment.Table, internshipenrollment.Columns, sqlgraph.NewFieldSpec(internshipenrollment.FieldID, field.TypeString)) + if ps := ieu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := ieu.mutation.Status(); ok { + _spec.SetField(internshipenrollment.FieldStatus, field.TypeString, value) + } + if value, ok := ieu.mutation.UpdatedAt(); ok { + _spec.SetField(internshipenrollment.FieldUpdatedAt, field.TypeTime, value) + } + if ieu.mutation.CreatedByCleared() { + _spec.ClearField(internshipenrollment.FieldCreatedBy, field.TypeString) + } + if value, ok := ieu.mutation.UpdatedBy(); ok { + _spec.SetField(internshipenrollment.FieldUpdatedBy, field.TypeString, value) + } + if ieu.mutation.UpdatedByCleared() { + _spec.ClearField(internshipenrollment.FieldUpdatedBy, field.TypeString) + } + if value, ok := ieu.mutation.Metadata(); ok { + _spec.SetField(internshipenrollment.FieldMetadata, field.TypeJSON, value) + } + if ieu.mutation.MetadataCleared() { + _spec.ClearField(internshipenrollment.FieldMetadata, field.TypeJSON) + } + if value, ok := ieu.mutation.UserID(); ok { + _spec.SetField(internshipenrollment.FieldUserID, field.TypeString, value) + } + if value, ok := ieu.mutation.InternshipID(); ok { + _spec.SetField(internshipenrollment.FieldInternshipID, field.TypeString, value) + } + if value, ok := ieu.mutation.InternshipBatchID(); ok { + _spec.SetField(internshipenrollment.FieldInternshipBatchID, field.TypeString, value) + } + if value, ok := ieu.mutation.EnrollmentStatus(); ok { + _spec.SetField(internshipenrollment.FieldEnrollmentStatus, field.TypeString, value) + } + if value, ok := ieu.mutation.PaymentStatus(); ok { + _spec.SetField(internshipenrollment.FieldPaymentStatus, field.TypeString, value) + } + if value, ok := ieu.mutation.EnrolledAt(); ok { + _spec.SetField(internshipenrollment.FieldEnrolledAt, field.TypeTime, value) + } + if ieu.mutation.EnrolledAtCleared() { + _spec.ClearField(internshipenrollment.FieldEnrolledAt, field.TypeTime) + } + if value, ok := ieu.mutation.PaymentID(); ok { + _spec.SetField(internshipenrollment.FieldPaymentID, field.TypeString, value) + } + if ieu.mutation.PaymentIDCleared() { + _spec.ClearField(internshipenrollment.FieldPaymentID, field.TypeString) + } + if value, ok := ieu.mutation.RefundedAt(); ok { + _spec.SetField(internshipenrollment.FieldRefundedAt, field.TypeTime, value) + } + if ieu.mutation.RefundedAtCleared() { + _spec.ClearField(internshipenrollment.FieldRefundedAt, field.TypeTime) + } + if value, ok := ieu.mutation.CancellationReason(); ok { + _spec.SetField(internshipenrollment.FieldCancellationReason, field.TypeString, value) + } + if ieu.mutation.CancellationReasonCleared() { + _spec.ClearField(internshipenrollment.FieldCancellationReason, field.TypeString) + } + if value, ok := ieu.mutation.RefundReason(); ok { + _spec.SetField(internshipenrollment.FieldRefundReason, field.TypeString, value) + } + if ieu.mutation.RefundReasonCleared() { + _spec.ClearField(internshipenrollment.FieldRefundReason, field.TypeString) + } + if value, ok := ieu.mutation.IdempotencyKey(); ok { + _spec.SetField(internshipenrollment.FieldIdempotencyKey, field.TypeString, value) + } + if ieu.mutation.IdempotencyKeyCleared() { + _spec.ClearField(internshipenrollment.FieldIdempotencyKey, field.TypeString) + } + if n, err = sqlgraph.UpdateNodes(ctx, ieu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{internshipenrollment.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + ieu.mutation.done = true + return n, nil +} + +// InternshipEnrollmentUpdateOne is the builder for updating a single InternshipEnrollment entity. +type InternshipEnrollmentUpdateOne struct { + config + fields []string + hooks []Hook + mutation *InternshipEnrollmentMutation +} + +// SetStatus sets the "status" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetStatus(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetStatus(s) + return ieuo +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableStatus(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetStatus(*s) + } + return ieuo +} + +// SetUpdatedAt sets the "updated_at" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetUpdatedAt(t time.Time) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetUpdatedAt(t) + return ieuo +} + +// SetUpdatedBy sets the "updated_by" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetUpdatedBy(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetUpdatedBy(s) + return ieuo +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableUpdatedBy(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetUpdatedBy(*s) + } + return ieuo +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (ieuo *InternshipEnrollmentUpdateOne) ClearUpdatedBy() *InternshipEnrollmentUpdateOne { + ieuo.mutation.ClearUpdatedBy() + return ieuo +} + +// SetMetadata sets the "metadata" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetMetadata(m map[string]string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetMetadata(m) + return ieuo +} + +// ClearMetadata clears the value of the "metadata" field. +func (ieuo *InternshipEnrollmentUpdateOne) ClearMetadata() *InternshipEnrollmentUpdateOne { + ieuo.mutation.ClearMetadata() + return ieuo +} + +// SetUserID sets the "user_id" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetUserID(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetUserID(s) + return ieuo +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableUserID(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetUserID(*s) + } + return ieuo +} + +// SetInternshipID sets the "internship_id" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetInternshipID(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetInternshipID(s) + return ieuo +} + +// SetNillableInternshipID sets the "internship_id" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableInternshipID(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetInternshipID(*s) + } + return ieuo +} + +// SetInternshipBatchID sets the "internship_batch_id" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetInternshipBatchID(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetInternshipBatchID(s) + return ieuo +} + +// SetNillableInternshipBatchID sets the "internship_batch_id" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableInternshipBatchID(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetInternshipBatchID(*s) + } + return ieuo +} + +// SetEnrollmentStatus sets the "enrollment_status" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetEnrollmentStatus(tes types.InternshipEnrollmentStatus) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetEnrollmentStatus(tes) + return ieuo +} + +// SetNillableEnrollmentStatus sets the "enrollment_status" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableEnrollmentStatus(tes *types.InternshipEnrollmentStatus) *InternshipEnrollmentUpdateOne { + if tes != nil { + ieuo.SetEnrollmentStatus(*tes) + } + return ieuo +} + +// SetPaymentStatus sets the "payment_status" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetPaymentStatus(ts types.PaymentStatus) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetPaymentStatus(ts) + return ieuo +} + +// SetNillablePaymentStatus sets the "payment_status" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillablePaymentStatus(ts *types.PaymentStatus) *InternshipEnrollmentUpdateOne { + if ts != nil { + ieuo.SetPaymentStatus(*ts) + } + return ieuo +} + +// SetEnrolledAt sets the "enrolled_at" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetEnrolledAt(t time.Time) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetEnrolledAt(t) + return ieuo +} + +// SetNillableEnrolledAt sets the "enrolled_at" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableEnrolledAt(t *time.Time) *InternshipEnrollmentUpdateOne { + if t != nil { + ieuo.SetEnrolledAt(*t) + } + return ieuo +} + +// ClearEnrolledAt clears the value of the "enrolled_at" field. +func (ieuo *InternshipEnrollmentUpdateOne) ClearEnrolledAt() *InternshipEnrollmentUpdateOne { + ieuo.mutation.ClearEnrolledAt() + return ieuo +} + +// SetPaymentID sets the "payment_id" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetPaymentID(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetPaymentID(s) + return ieuo +} + +// SetNillablePaymentID sets the "payment_id" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillablePaymentID(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetPaymentID(*s) + } + return ieuo +} + +// ClearPaymentID clears the value of the "payment_id" field. +func (ieuo *InternshipEnrollmentUpdateOne) ClearPaymentID() *InternshipEnrollmentUpdateOne { + ieuo.mutation.ClearPaymentID() + return ieuo +} + +// SetRefundedAt sets the "refunded_at" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetRefundedAt(t time.Time) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetRefundedAt(t) + return ieuo +} + +// SetNillableRefundedAt sets the "refunded_at" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableRefundedAt(t *time.Time) *InternshipEnrollmentUpdateOne { + if t != nil { + ieuo.SetRefundedAt(*t) + } + return ieuo +} + +// ClearRefundedAt clears the value of the "refunded_at" field. +func (ieuo *InternshipEnrollmentUpdateOne) ClearRefundedAt() *InternshipEnrollmentUpdateOne { + ieuo.mutation.ClearRefundedAt() + return ieuo +} + +// SetCancellationReason sets the "cancellation_reason" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetCancellationReason(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetCancellationReason(s) + return ieuo +} + +// SetNillableCancellationReason sets the "cancellation_reason" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableCancellationReason(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetCancellationReason(*s) + } + return ieuo +} + +// ClearCancellationReason clears the value of the "cancellation_reason" field. +func (ieuo *InternshipEnrollmentUpdateOne) ClearCancellationReason() *InternshipEnrollmentUpdateOne { + ieuo.mutation.ClearCancellationReason() + return ieuo +} + +// SetRefundReason sets the "refund_reason" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetRefundReason(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetRefundReason(s) + return ieuo +} + +// SetNillableRefundReason sets the "refund_reason" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableRefundReason(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetRefundReason(*s) + } + return ieuo +} + +// ClearRefundReason clears the value of the "refund_reason" field. +func (ieuo *InternshipEnrollmentUpdateOne) ClearRefundReason() *InternshipEnrollmentUpdateOne { + ieuo.mutation.ClearRefundReason() + return ieuo +} + +// SetIdempotencyKey sets the "idempotency_key" field. +func (ieuo *InternshipEnrollmentUpdateOne) SetIdempotencyKey(s string) *InternshipEnrollmentUpdateOne { + ieuo.mutation.SetIdempotencyKey(s) + return ieuo +} + +// SetNillableIdempotencyKey sets the "idempotency_key" field if the given value is not nil. +func (ieuo *InternshipEnrollmentUpdateOne) SetNillableIdempotencyKey(s *string) *InternshipEnrollmentUpdateOne { + if s != nil { + ieuo.SetIdempotencyKey(*s) + } + return ieuo +} + +// ClearIdempotencyKey clears the value of the "idempotency_key" field. +func (ieuo *InternshipEnrollmentUpdateOne) ClearIdempotencyKey() *InternshipEnrollmentUpdateOne { + ieuo.mutation.ClearIdempotencyKey() + return ieuo +} + +// Mutation returns the InternshipEnrollmentMutation object of the builder. +func (ieuo *InternshipEnrollmentUpdateOne) Mutation() *InternshipEnrollmentMutation { + return ieuo.mutation +} + +// Where appends a list predicates to the InternshipEnrollmentUpdate builder. +func (ieuo *InternshipEnrollmentUpdateOne) Where(ps ...predicate.InternshipEnrollment) *InternshipEnrollmentUpdateOne { + ieuo.mutation.Where(ps...) + return ieuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (ieuo *InternshipEnrollmentUpdateOne) Select(field string, fields ...string) *InternshipEnrollmentUpdateOne { + ieuo.fields = append([]string{field}, fields...) + return ieuo +} + +// Save executes the query and returns the updated InternshipEnrollment entity. +func (ieuo *InternshipEnrollmentUpdateOne) Save(ctx context.Context) (*InternshipEnrollment, error) { + ieuo.defaults() + return withHooks(ctx, ieuo.sqlSave, ieuo.mutation, ieuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ieuo *InternshipEnrollmentUpdateOne) SaveX(ctx context.Context) *InternshipEnrollment { + node, err := ieuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (ieuo *InternshipEnrollmentUpdateOne) Exec(ctx context.Context) error { + _, err := ieuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ieuo *InternshipEnrollmentUpdateOne) ExecX(ctx context.Context) { + if err := ieuo.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (ieuo *InternshipEnrollmentUpdateOne) defaults() { + if _, ok := ieuo.mutation.UpdatedAt(); !ok { + v := internshipenrollment.UpdateDefaultUpdatedAt() + ieuo.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (ieuo *InternshipEnrollmentUpdateOne) check() error { + if v, ok := ieuo.mutation.UserID(); ok { + if err := internshipenrollment.UserIDValidator(v); err != nil { + return &ValidationError{Name: "user_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.user_id": %w`, err)} + } + } + if v, ok := ieuo.mutation.InternshipID(); ok { + if err := internshipenrollment.InternshipIDValidator(v); err != nil { + return &ValidationError{Name: "internship_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.internship_id": %w`, err)} + } + } + if v, ok := ieuo.mutation.InternshipBatchID(); ok { + if err := internshipenrollment.InternshipBatchIDValidator(v); err != nil { + return &ValidationError{Name: "internship_batch_id", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.internship_batch_id": %w`, err)} + } + } + if v, ok := ieuo.mutation.EnrollmentStatus(); ok { + if err := internshipenrollment.EnrollmentStatusValidator(string(v)); err != nil { + return &ValidationError{Name: "enrollment_status", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.enrollment_status": %w`, err)} + } + } + if v, ok := ieuo.mutation.PaymentStatus(); ok { + if err := internshipenrollment.PaymentStatusValidator(string(v)); err != nil { + return &ValidationError{Name: "payment_status", err: fmt.Errorf(`ent: validator failed for field "InternshipEnrollment.payment_status": %w`, err)} + } + } + return nil +} + +func (ieuo *InternshipEnrollmentUpdateOne) sqlSave(ctx context.Context) (_node *InternshipEnrollment, err error) { + if err := ieuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(internshipenrollment.Table, internshipenrollment.Columns, sqlgraph.NewFieldSpec(internshipenrollment.FieldID, field.TypeString)) + id, ok := ieuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "InternshipEnrollment.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := ieuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, internshipenrollment.FieldID) + for _, f := range fields { + if !internshipenrollment.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != internshipenrollment.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := ieuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := ieuo.mutation.Status(); ok { + _spec.SetField(internshipenrollment.FieldStatus, field.TypeString, value) + } + if value, ok := ieuo.mutation.UpdatedAt(); ok { + _spec.SetField(internshipenrollment.FieldUpdatedAt, field.TypeTime, value) + } + if ieuo.mutation.CreatedByCleared() { + _spec.ClearField(internshipenrollment.FieldCreatedBy, field.TypeString) + } + if value, ok := ieuo.mutation.UpdatedBy(); ok { + _spec.SetField(internshipenrollment.FieldUpdatedBy, field.TypeString, value) + } + if ieuo.mutation.UpdatedByCleared() { + _spec.ClearField(internshipenrollment.FieldUpdatedBy, field.TypeString) + } + if value, ok := ieuo.mutation.Metadata(); ok { + _spec.SetField(internshipenrollment.FieldMetadata, field.TypeJSON, value) + } + if ieuo.mutation.MetadataCleared() { + _spec.ClearField(internshipenrollment.FieldMetadata, field.TypeJSON) + } + if value, ok := ieuo.mutation.UserID(); ok { + _spec.SetField(internshipenrollment.FieldUserID, field.TypeString, value) + } + if value, ok := ieuo.mutation.InternshipID(); ok { + _spec.SetField(internshipenrollment.FieldInternshipID, field.TypeString, value) + } + if value, ok := ieuo.mutation.InternshipBatchID(); ok { + _spec.SetField(internshipenrollment.FieldInternshipBatchID, field.TypeString, value) + } + if value, ok := ieuo.mutation.EnrollmentStatus(); ok { + _spec.SetField(internshipenrollment.FieldEnrollmentStatus, field.TypeString, value) + } + if value, ok := ieuo.mutation.PaymentStatus(); ok { + _spec.SetField(internshipenrollment.FieldPaymentStatus, field.TypeString, value) + } + if value, ok := ieuo.mutation.EnrolledAt(); ok { + _spec.SetField(internshipenrollment.FieldEnrolledAt, field.TypeTime, value) + } + if ieuo.mutation.EnrolledAtCleared() { + _spec.ClearField(internshipenrollment.FieldEnrolledAt, field.TypeTime) + } + if value, ok := ieuo.mutation.PaymentID(); ok { + _spec.SetField(internshipenrollment.FieldPaymentID, field.TypeString, value) + } + if ieuo.mutation.PaymentIDCleared() { + _spec.ClearField(internshipenrollment.FieldPaymentID, field.TypeString) + } + if value, ok := ieuo.mutation.RefundedAt(); ok { + _spec.SetField(internshipenrollment.FieldRefundedAt, field.TypeTime, value) + } + if ieuo.mutation.RefundedAtCleared() { + _spec.ClearField(internshipenrollment.FieldRefundedAt, field.TypeTime) + } + if value, ok := ieuo.mutation.CancellationReason(); ok { + _spec.SetField(internshipenrollment.FieldCancellationReason, field.TypeString, value) + } + if ieuo.mutation.CancellationReasonCleared() { + _spec.ClearField(internshipenrollment.FieldCancellationReason, field.TypeString) + } + if value, ok := ieuo.mutation.RefundReason(); ok { + _spec.SetField(internshipenrollment.FieldRefundReason, field.TypeString, value) + } + if ieuo.mutation.RefundReasonCleared() { + _spec.ClearField(internshipenrollment.FieldRefundReason, field.TypeString) + } + if value, ok := ieuo.mutation.IdempotencyKey(); ok { + _spec.SetField(internshipenrollment.FieldIdempotencyKey, field.TypeString, value) + } + if ieuo.mutation.IdempotencyKeyCleared() { + _spec.ClearField(internshipenrollment.FieldIdempotencyKey, field.TypeString) + } + _node = &InternshipEnrollment{config: ieuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, ieuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{internshipenrollment.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + ieuo.mutation.done = true + return _node, nil +} diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 323853b..d856d2f 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -9,6 +9,70 @@ import ( ) var ( + // CartsColumns holds the columns for the "carts" table. + CartsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "status", Type: field.TypeString, Default: "published", SchemaType: map[string]string{"postgres": "varchar(20)"}}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "updated_by", Type: field.TypeString, Nullable: true}, + {Name: "metadata", Type: field.TypeJSON, Nullable: true}, + {Name: "type", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "subtotal", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "discount_amount", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "tax_amount", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "total", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "expires_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamp"}}, + {Name: "user_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + } + // CartsTable holds the schema information for the "carts" table. + CartsTable = &schema.Table{ + Name: "carts", + Columns: CartsColumns, + PrimaryKey: []*schema.Column{CartsColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "carts_users_carts", + Columns: []*schema.Column{CartsColumns[13]}, + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + } + // CartLineItemsColumns holds the columns for the "cart_line_items" table. + CartLineItemsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "status", Type: field.TypeString, Default: "published", SchemaType: map[string]string{"postgres": "varchar(20)"}}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "updated_by", Type: field.TypeString, Nullable: true}, + {Name: "metadata", Type: field.TypeJSON, Nullable: true}, + {Name: "entity_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "entity_type", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "quantity", Type: field.TypeInt, Default: 1}, + {Name: "per_unit_price", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "tax_amount", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "discount_amount", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "subtotal", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "total", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, + {Name: "cart_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + } + // CartLineItemsTable holds the schema information for the "cart_line_items" table. + CartLineItemsTable = &schema.Table{ + Name: "cart_line_items", + Columns: CartLineItemsColumns, + PrimaryKey: []*schema.Column{CartLineItemsColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "cart_line_items_carts_line_items", + Columns: []*schema.Column{CartLineItemsColumns[15]}, + RefColumns: []*schema.Column{CartsColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + } // CategoriesColumns holds the columns for the "categories" table. CategoriesColumns = []*schema.Column{ {Name: "id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, @@ -17,6 +81,7 @@ var ( {Name: "updated_at", Type: field.TypeTime}, {Name: "created_by", Type: field.TypeString, Nullable: true}, {Name: "updated_by", Type: field.TypeString, Nullable: true}, + {Name: "metadata", Type: field.TypeJSON, Nullable: true}, {Name: "name", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, {Name: "lookup_key", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, {Name: "description", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}}, @@ -30,7 +95,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "categories_internships_categories", - Columns: []*schema.Column{CategoriesColumns[9]}, + Columns: []*schema.Column{CategoriesColumns[10]}, RefColumns: []*schema.Column{InternshipsColumns[0]}, OnDelete: schema.SetNull, }, @@ -39,7 +104,7 @@ var ( { Name: "category_name", Unique: true, - Columns: []*schema.Column{CategoriesColumns[6]}, + Columns: []*schema.Column{CategoriesColumns[7]}, }, }, } @@ -113,9 +178,11 @@ var ( {Name: "prerequisites", Type: field.TypeJSON, Nullable: true}, {Name: "benefits", Type: field.TypeJSON, Nullable: true}, {Name: "currency", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(255)"}}, - {Name: "price", Type: field.TypeOther, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(10,2)"}}, + {Name: "price", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "decimal(10,2)"}}, {Name: "flat_discount", Type: field.TypeOther, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(10,2)"}}, {Name: "percentage_discount", Type: field.TypeOther, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(10,2)"}}, + {Name: "subtotal", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "decimal(10,2)"}}, + {Name: "total", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "decimal(10,2)"}}, {Name: "category_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(255)"}}, } // InternshipsTable holds the schema information for the "internships" table. @@ -126,12 +193,71 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "internships_categories_internships", - Columns: []*schema.Column{InternshipsColumns[20]}, + Columns: []*schema.Column{InternshipsColumns[22]}, RefColumns: []*schema.Column{CategoriesColumns[0]}, OnDelete: schema.SetNull, }, }, } + // InternshipBatchesColumns holds the columns for the "internship_batches" table. + InternshipBatchesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeString, Unique: true, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "status", Type: field.TypeString, Default: "published", SchemaType: map[string]string{"postgres": "varchar(20)"}}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "updated_by", Type: field.TypeString, Nullable: true}, + {Name: "metadata", Type: field.TypeJSON, Nullable: true}, + {Name: "internship_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "name", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "description", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "start_date", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamp"}}, + {Name: "end_date", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamp"}}, + {Name: "batch_status", Type: field.TypeString, Default: "upcoming", SchemaType: map[string]string{"postgres": "varchar(255)"}}, + } + // InternshipBatchesTable holds the schema information for the "internship_batches" table. + InternshipBatchesTable = &schema.Table{ + Name: "internship_batches", + Columns: InternshipBatchesColumns, + PrimaryKey: []*schema.Column{InternshipBatchesColumns[0]}, + } + // InternshipEnrollmentsColumns holds the columns for the "internship_enrollments" table. + InternshipEnrollmentsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "status", Type: field.TypeString, Default: "published", SchemaType: map[string]string{"postgres": "varchar(20)"}}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "updated_by", Type: field.TypeString, Nullable: true}, + {Name: "metadata", Type: field.TypeJSON, Nullable: true}, + {Name: "user_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "internship_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "internship_batch_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "enrollment_status", Type: field.TypeString, Default: "pending", SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "payment_status", Type: field.TypeString, Default: "pending", SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "enrolled_at", Type: field.TypeTime, Nullable: true}, + {Name: "payment_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + {Name: "refunded_at", Type: field.TypeTime, Nullable: true}, + {Name: "cancellation_reason", Type: field.TypeString, Nullable: true}, + {Name: "refund_reason", Type: field.TypeString, Nullable: true}, + {Name: "idempotency_key", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(255)"}}, + } + // InternshipEnrollmentsTable holds the schema information for the "internship_enrollments" table. + InternshipEnrollmentsTable = &schema.Table{ + Name: "internship_enrollments", + Columns: InternshipEnrollmentsColumns, + PrimaryKey: []*schema.Column{InternshipEnrollmentsColumns[0]}, + } + // OrdersColumns holds the columns for the "orders" table. + OrdersColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + } + // OrdersTable holds the schema information for the "orders" table. + OrdersTable = &schema.Table{ + Name: "orders", + Columns: OrdersColumns, + PrimaryKey: []*schema.Column{OrdersColumns[0]}, + } // PaymentsColumns holds the columns for the "payments" table. PaymentsColumns = []*schema.Column{ {Name: "id", Type: field.TypeString, Unique: true, SchemaType: map[string]string{"postgres": "varchar(50)"}}, @@ -143,14 +269,14 @@ var ( {Name: "idempotency_key", Type: field.TypeString, Unique: true, SchemaType: map[string]string{"postgres": "varchar(50)"}}, {Name: "destination_type", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(50)"}}, {Name: "destination_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(50)"}}, - {Name: "payment_method_type", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(50)"}}, + {Name: "payment_method_type", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(50)"}}, {Name: "payment_method_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(50)"}}, - {Name: "payment_gateway_provider", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(50)"}}, + {Name: "payment_gateway_provider", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(50)"}}, {Name: "gateway_payment_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(255)"}}, {Name: "amount", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric(20,8)"}}, {Name: "currency", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(10)"}}, - {Name: "payment_status", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(50)"}}, - {Name: "track_attempts", Type: field.TypeBool, Default: false}, + {Name: "payment_status", Type: field.TypeString, Default: "pending", SchemaType: map[string]string{"postgres": "varchar(50)"}}, + {Name: "track_attempts", Type: field.TypeBool, Default: true}, {Name: "metadata", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}}, {Name: "succeeded_at", Type: field.TypeTime, Nullable: true}, {Name: "failed_at", Type: field.TypeTime, Nullable: true}, @@ -270,10 +396,15 @@ var ( } // Tables holds all the tables in the schema. Tables = []*schema.Table{ + CartsTable, + CartLineItemsTable, CategoriesTable, DiscountsTable, FileUploadsTable, InternshipsTable, + InternshipBatchesTable, + InternshipEnrollmentsTable, + OrdersTable, PaymentsTable, PaymentAttemptsTable, UsersTable, @@ -281,6 +412,8 @@ var ( ) func init() { + CartsTable.ForeignKeys[0].RefTable = UsersTable + CartLineItemsTable.ForeignKeys[0].RefTable = CartsTable CategoriesTable.ForeignKeys[0].RefTable = InternshipsTable InternshipsTable.ForeignKeys[0].RefTable = CategoriesTable PaymentAttemptsTable.ForeignKeys[0].RefTable = PaymentsTable diff --git a/ent/mixin/metadata.go b/ent/mixin/metadata.go new file mode 100644 index 0000000..d3be184 --- /dev/null +++ b/ent/mixin/metadata.go @@ -0,0 +1,28 @@ +package mixin + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/mixin" +) + +// BaseMixin implements the ent.Mixin for sharing base fields with package schemas. +type MetadataMixin struct { + mixin.Schema +} + +// Fields of the BaseMixin. +func (MetadataMixin) Fields() []ent.Field { + return []ent.Field{ + field.JSON("metadata", map[string]string{}). + Default(map[string]string{}). + Optional(), + } +} + +// Hooks of the BaseMixin. +func (MetadataMixin) Hooks() []ent.Hook { + return []ent.Hook{ + // Add hooks for updating updated_at and updated_by + } +} diff --git a/ent/mutation.go b/ent/mutation.go index c824ad4..1f7f20e 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -11,10 +11,14 @@ import ( "entgo.io/ent" "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" "github.com/omkar273/codegeeky/ent/category" "github.com/omkar273/codegeeky/ent/discount" "github.com/omkar273/codegeeky/ent/fileupload" "github.com/omkar273/codegeeky/ent/internship" + "github.com/omkar273/codegeeky/ent/internshipbatch" + "github.com/omkar273/codegeeky/ent/internshipenrollment" "github.com/omkar273/codegeeky/ent/payment" "github.com/omkar273/codegeeky/ent/paymentattempt" "github.com/omkar273/codegeeky/ent/predicate" @@ -32,49 +36,60 @@ const ( OpUpdateOne = ent.OpUpdateOne // Node types. - TypeCategory = "Category" - TypeDiscount = "Discount" - TypeFileUpload = "FileUpload" - TypeInternship = "Internship" - TypePayment = "Payment" - TypePaymentAttempt = "PaymentAttempt" - TypeUser = "User" + TypeCart = "Cart" + TypeCartLineItems = "CartLineItems" + TypeCategory = "Category" + TypeDiscount = "Discount" + TypeFileUpload = "FileUpload" + TypeInternship = "Internship" + TypeInternshipBatch = "InternshipBatch" + TypeInternshipEnrollment = "InternshipEnrollment" + TypeOrder = "Order" + TypePayment = "Payment" + TypePaymentAttempt = "PaymentAttempt" + TypeUser = "User" ) -// CategoryMutation represents an operation that mutates the Category nodes in the graph. -type CategoryMutation struct { +// CartMutation represents an operation that mutates the Cart nodes in the graph. +type CartMutation struct { config - op Op - typ string - id *string - status *string - created_at *time.Time - updated_at *time.Time - created_by *string - updated_by *string - name *string - lookup_key *string - description *string - clearedFields map[string]struct{} - internships map[string]struct{} - removedinternships map[string]struct{} - clearedinternships bool - done bool - oldValue func(context.Context) (*Category, error) - predicates []predicate.Category -} - -var _ ent.Mutation = (*CategoryMutation)(nil) - -// categoryOption allows management of the mutation configuration using functional options. -type categoryOption func(*CategoryMutation) - -// newCategoryMutation creates new mutation for the Category entity. -func newCategoryMutation(c config, op Op, opts ...categoryOption) *CategoryMutation { - m := &CategoryMutation{ + op Op + typ string + id *string + status *string + created_at *time.Time + updated_at *time.Time + created_by *string + updated_by *string + metadata *map[string]string + _type *string + subtotal *decimal.Decimal + discount_amount *decimal.Decimal + tax_amount *decimal.Decimal + total *decimal.Decimal + expires_at *time.Time + clearedFields map[string]struct{} + line_items map[string]struct{} + removedline_items map[string]struct{} + clearedline_items bool + user *string + cleareduser bool + done bool + oldValue func(context.Context) (*Cart, error) + predicates []predicate.Cart +} + +var _ ent.Mutation = (*CartMutation)(nil) + +// cartOption allows management of the mutation configuration using functional options. +type cartOption func(*CartMutation) + +// newCartMutation creates new mutation for the Cart entity. +func newCartMutation(c config, op Op, opts ...cartOption) *CartMutation { + m := &CartMutation{ config: c, op: op, - typ: TypeCategory, + typ: TypeCart, clearedFields: make(map[string]struct{}), } for _, opt := range opts { @@ -83,20 +98,20 @@ func newCategoryMutation(c config, op Op, opts ...categoryOption) *CategoryMutat return m } -// withCategoryID sets the ID field of the mutation. -func withCategoryID(id string) categoryOption { - return func(m *CategoryMutation) { +// withCartID sets the ID field of the mutation. +func withCartID(id string) cartOption { + return func(m *CartMutation) { var ( err error once sync.Once - value *Category + value *Cart ) - m.oldValue = func(ctx context.Context) (*Category, error) { + m.oldValue = func(ctx context.Context) (*Cart, error) { once.Do(func() { if m.done { err = errors.New("querying old values post mutation is not allowed") } else { - value, err = m.Client().Category.Get(ctx, id) + value, err = m.Client().Cart.Get(ctx, id) } }) return value, err @@ -105,10 +120,10 @@ func withCategoryID(id string) categoryOption { } } -// withCategory sets the old Category of the mutation. -func withCategory(node *Category) categoryOption { - return func(m *CategoryMutation) { - m.oldValue = func(context.Context) (*Category, error) { +// withCart sets the old Cart of the mutation. +func withCart(node *Cart) cartOption { + return func(m *CartMutation) { + m.oldValue = func(context.Context) (*Cart, error) { return node, nil } m.id = &node.ID @@ -117,7 +132,7 @@ func withCategory(node *Category) categoryOption { // Client returns a new `ent.Client` from the mutation. If the mutation was // executed in a transaction (ent.Tx), a transactional client is returned. -func (m CategoryMutation) Client() *Client { +func (m CartMutation) Client() *Client { client := &Client{config: m.config} client.init() return client @@ -125,7 +140,7 @@ func (m CategoryMutation) Client() *Client { // Tx returns an `ent.Tx` for mutations that were executed in transactions; // it returns an error otherwise. -func (m CategoryMutation) Tx() (*Tx, error) { +func (m CartMutation) Tx() (*Tx, error) { if _, ok := m.driver.(*txDriver); !ok { return nil, errors.New("ent: mutation is not running in a transaction") } @@ -135,14 +150,14 @@ func (m CategoryMutation) Tx() (*Tx, error) { } // SetID sets the value of the id field. Note that this -// operation is only accepted on creation of Category entities. -func (m *CategoryMutation) SetID(id string) { +// operation is only accepted on creation of Cart entities. +func (m *CartMutation) SetID(id string) { m.id = &id } // ID returns the ID value in the mutation. Note that the ID is only available // if it was provided to the builder or after it was returned from the database. -func (m *CategoryMutation) ID() (id string, exists bool) { +func (m *CartMutation) ID() (id string, exists bool) { if m.id == nil { return } @@ -153,7 +168,7 @@ func (m *CategoryMutation) ID() (id string, exists bool) { // That means, if the mutation is applied within a transaction with an isolation level such // as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated // or updated by the mutation. -func (m *CategoryMutation) IDs(ctx context.Context) ([]string, error) { +func (m *CartMutation) IDs(ctx context.Context) ([]string, error) { switch { case m.op.Is(OpUpdateOne | OpDeleteOne): id, exists := m.ID() @@ -162,19 +177,19 @@ func (m *CategoryMutation) IDs(ctx context.Context) ([]string, error) { } fallthrough case m.op.Is(OpUpdate | OpDelete): - return m.Client().Category.Query().Where(m.predicates...).IDs(ctx) + return m.Client().Cart.Query().Where(m.predicates...).IDs(ctx) default: return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) } } // SetStatus sets the "status" field. -func (m *CategoryMutation) SetStatus(s string) { +func (m *CartMutation) SetStatus(s string) { m.status = &s } // Status returns the value of the "status" field in the mutation. -func (m *CategoryMutation) Status() (r string, exists bool) { +func (m *CartMutation) Status() (r string, exists bool) { v := m.status if v == nil { return @@ -182,10 +197,10 @@ func (m *CategoryMutation) Status() (r string, exists bool) { return *v, true } -// OldStatus returns the old "status" field's value of the Category entity. -// If the Category object wasn't provided to the builder, the object is fetched from the database. +// OldStatus returns the old "status" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *CategoryMutation) OldStatus(ctx context.Context) (v string, err error) { +func (m *CartMutation) OldStatus(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldStatus is only allowed on UpdateOne operations") } @@ -200,17 +215,17 @@ func (m *CategoryMutation) OldStatus(ctx context.Context) (v string, err error) } // ResetStatus resets all changes to the "status" field. -func (m *CategoryMutation) ResetStatus() { +func (m *CartMutation) ResetStatus() { m.status = nil } // SetCreatedAt sets the "created_at" field. -func (m *CategoryMutation) SetCreatedAt(t time.Time) { +func (m *CartMutation) SetCreatedAt(t time.Time) { m.created_at = &t } // CreatedAt returns the value of the "created_at" field in the mutation. -func (m *CategoryMutation) CreatedAt() (r time.Time, exists bool) { +func (m *CartMutation) CreatedAt() (r time.Time, exists bool) { v := m.created_at if v == nil { return @@ -218,10 +233,10 @@ func (m *CategoryMutation) CreatedAt() (r time.Time, exists bool) { return *v, true } -// OldCreatedAt returns the old "created_at" field's value of the Category entity. -// If the Category object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedAt returns the old "created_at" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *CategoryMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { +func (m *CartMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") } @@ -236,17 +251,17 @@ func (m *CategoryMutation) OldCreatedAt(ctx context.Context) (v time.Time, err e } // ResetCreatedAt resets all changes to the "created_at" field. -func (m *CategoryMutation) ResetCreatedAt() { +func (m *CartMutation) ResetCreatedAt() { m.created_at = nil } // SetUpdatedAt sets the "updated_at" field. -func (m *CategoryMutation) SetUpdatedAt(t time.Time) { +func (m *CartMutation) SetUpdatedAt(t time.Time) { m.updated_at = &t } // UpdatedAt returns the value of the "updated_at" field in the mutation. -func (m *CategoryMutation) UpdatedAt() (r time.Time, exists bool) { +func (m *CartMutation) UpdatedAt() (r time.Time, exists bool) { v := m.updated_at if v == nil { return @@ -254,10 +269,10 @@ func (m *CategoryMutation) UpdatedAt() (r time.Time, exists bool) { return *v, true } -// OldUpdatedAt returns the old "updated_at" field's value of the Category entity. -// If the Category object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedAt returns the old "updated_at" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *CategoryMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { +func (m *CartMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") } @@ -272,17 +287,17 @@ func (m *CategoryMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err e } // ResetUpdatedAt resets all changes to the "updated_at" field. -func (m *CategoryMutation) ResetUpdatedAt() { +func (m *CartMutation) ResetUpdatedAt() { m.updated_at = nil } // SetCreatedBy sets the "created_by" field. -func (m *CategoryMutation) SetCreatedBy(s string) { +func (m *CartMutation) SetCreatedBy(s string) { m.created_by = &s } // CreatedBy returns the value of the "created_by" field in the mutation. -func (m *CategoryMutation) CreatedBy() (r string, exists bool) { +func (m *CartMutation) CreatedBy() (r string, exists bool) { v := m.created_by if v == nil { return @@ -290,10 +305,10 @@ func (m *CategoryMutation) CreatedBy() (r string, exists bool) { return *v, true } -// OldCreatedBy returns the old "created_by" field's value of the Category entity. -// If the Category object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedBy returns the old "created_by" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *CategoryMutation) OldCreatedBy(ctx context.Context) (v string, err error) { +func (m *CartMutation) OldCreatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") } @@ -308,30 +323,30 @@ func (m *CategoryMutation) OldCreatedBy(ctx context.Context) (v string, err erro } // ClearCreatedBy clears the value of the "created_by" field. -func (m *CategoryMutation) ClearCreatedBy() { +func (m *CartMutation) ClearCreatedBy() { m.created_by = nil - m.clearedFields[category.FieldCreatedBy] = struct{}{} + m.clearedFields[cart.FieldCreatedBy] = struct{}{} } // CreatedByCleared returns if the "created_by" field was cleared in this mutation. -func (m *CategoryMutation) CreatedByCleared() bool { - _, ok := m.clearedFields[category.FieldCreatedBy] +func (m *CartMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[cart.FieldCreatedBy] return ok } // ResetCreatedBy resets all changes to the "created_by" field. -func (m *CategoryMutation) ResetCreatedBy() { +func (m *CartMutation) ResetCreatedBy() { m.created_by = nil - delete(m.clearedFields, category.FieldCreatedBy) + delete(m.clearedFields, cart.FieldCreatedBy) } // SetUpdatedBy sets the "updated_by" field. -func (m *CategoryMutation) SetUpdatedBy(s string) { +func (m *CartMutation) SetUpdatedBy(s string) { m.updated_by = &s } // UpdatedBy returns the value of the "updated_by" field in the mutation. -func (m *CategoryMutation) UpdatedBy() (r string, exists bool) { +func (m *CartMutation) UpdatedBy() (r string, exists bool) { v := m.updated_by if v == nil { return @@ -339,10 +354,10 @@ func (m *CategoryMutation) UpdatedBy() (r string, exists bool) { return *v, true } -// OldUpdatedBy returns the old "updated_by" field's value of the Category entity. -// If the Category object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedBy returns the old "updated_by" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *CategoryMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { +func (m *CartMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") } @@ -357,256 +372,478 @@ func (m *CategoryMutation) OldUpdatedBy(ctx context.Context) (v string, err erro } // ClearUpdatedBy clears the value of the "updated_by" field. -func (m *CategoryMutation) ClearUpdatedBy() { +func (m *CartMutation) ClearUpdatedBy() { m.updated_by = nil - m.clearedFields[category.FieldUpdatedBy] = struct{}{} + m.clearedFields[cart.FieldUpdatedBy] = struct{}{} } // UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. -func (m *CategoryMutation) UpdatedByCleared() bool { - _, ok := m.clearedFields[category.FieldUpdatedBy] +func (m *CartMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[cart.FieldUpdatedBy] return ok } // ResetUpdatedBy resets all changes to the "updated_by" field. -func (m *CategoryMutation) ResetUpdatedBy() { +func (m *CartMutation) ResetUpdatedBy() { m.updated_by = nil - delete(m.clearedFields, category.FieldUpdatedBy) + delete(m.clearedFields, cart.FieldUpdatedBy) } -// SetName sets the "name" field. -func (m *CategoryMutation) SetName(s string) { - m.name = &s +// SetMetadata sets the "metadata" field. +func (m *CartMutation) SetMetadata(value map[string]string) { + m.metadata = &value } -// Name returns the value of the "name" field in the mutation. -func (m *CategoryMutation) Name() (r string, exists bool) { - v := m.name +// Metadata returns the value of the "metadata" field in the mutation. +func (m *CartMutation) Metadata() (r map[string]string, exists bool) { + v := m.metadata if v == nil { return } return *v, true } -// OldName returns the old "name" field's value of the Category entity. -// If the Category object wasn't provided to the builder, the object is fetched from the database. +// OldMetadata returns the old "metadata" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *CategoryMutation) OldName(ctx context.Context) (v string, err error) { +func (m *CartMutation) OldMetadata(ctx context.Context) (v map[string]string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldName is only allowed on UpdateOne operations") + return v, errors.New("OldMetadata is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldName requires an ID field in the mutation") + return v, errors.New("OldMetadata requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldName: %w", err) + return v, fmt.Errorf("querying old value for OldMetadata: %w", err) } - return oldValue.Name, nil + return oldValue.Metadata, nil } -// ResetName resets all changes to the "name" field. -func (m *CategoryMutation) ResetName() { - m.name = nil +// ClearMetadata clears the value of the "metadata" field. +func (m *CartMutation) ClearMetadata() { + m.metadata = nil + m.clearedFields[cart.FieldMetadata] = struct{}{} } -// SetLookupKey sets the "lookup_key" field. -func (m *CategoryMutation) SetLookupKey(s string) { - m.lookup_key = &s +// MetadataCleared returns if the "metadata" field was cleared in this mutation. +func (m *CartMutation) MetadataCleared() bool { + _, ok := m.clearedFields[cart.FieldMetadata] + return ok } -// LookupKey returns the value of the "lookup_key" field in the mutation. -func (m *CategoryMutation) LookupKey() (r string, exists bool) { - v := m.lookup_key +// ResetMetadata resets all changes to the "metadata" field. +func (m *CartMutation) ResetMetadata() { + m.metadata = nil + delete(m.clearedFields, cart.FieldMetadata) +} + +// SetUserID sets the "user_id" field. +func (m *CartMutation) SetUserID(s string) { + m.user = &s +} + +// UserID returns the value of the "user_id" field in the mutation. +func (m *CartMutation) UserID() (r string, exists bool) { + v := m.user if v == nil { return } return *v, true } -// OldLookupKey returns the old "lookup_key" field's value of the Category entity. -// If the Category object wasn't provided to the builder, the object is fetched from the database. +// OldUserID returns the old "user_id" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *CategoryMutation) OldLookupKey(ctx context.Context) (v string, err error) { +func (m *CartMutation) OldUserID(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLookupKey is only allowed on UpdateOne operations") + return v, errors.New("OldUserID is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLookupKey requires an ID field in the mutation") + return v, errors.New("OldUserID requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldLookupKey: %w", err) + return v, fmt.Errorf("querying old value for OldUserID: %w", err) } - return oldValue.LookupKey, nil + return oldValue.UserID, nil } -// ResetLookupKey resets all changes to the "lookup_key" field. -func (m *CategoryMutation) ResetLookupKey() { - m.lookup_key = nil +// ResetUserID resets all changes to the "user_id" field. +func (m *CartMutation) ResetUserID() { + m.user = nil } -// SetDescription sets the "description" field. -func (m *CategoryMutation) SetDescription(s string) { - m.description = &s +// SetType sets the "type" field. +func (m *CartMutation) SetType(s string) { + m._type = &s } -// Description returns the value of the "description" field in the mutation. -func (m *CategoryMutation) Description() (r string, exists bool) { - v := m.description +// GetType returns the value of the "type" field in the mutation. +func (m *CartMutation) GetType() (r string, exists bool) { + v := m._type if v == nil { return } return *v, true } -// OldDescription returns the old "description" field's value of the Category entity. -// If the Category object wasn't provided to the builder, the object is fetched from the database. +// OldType returns the old "type" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *CategoryMutation) OldDescription(ctx context.Context) (v string, err error) { +func (m *CartMutation) OldType(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldDescription is only allowed on UpdateOne operations") + return v, errors.New("OldType is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldDescription requires an ID field in the mutation") + return v, errors.New("OldType requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldDescription: %w", err) + return v, fmt.Errorf("querying old value for OldType: %w", err) } - return oldValue.Description, nil + return oldValue.Type, nil } -// ClearDescription clears the value of the "description" field. -func (m *CategoryMutation) ClearDescription() { - m.description = nil - m.clearedFields[category.FieldDescription] = struct{}{} +// ResetType resets all changes to the "type" field. +func (m *CartMutation) ResetType() { + m._type = nil } -// DescriptionCleared returns if the "description" field was cleared in this mutation. -func (m *CategoryMutation) DescriptionCleared() bool { - _, ok := m.clearedFields[category.FieldDescription] - return ok +// SetSubtotal sets the "subtotal" field. +func (m *CartMutation) SetSubtotal(d decimal.Decimal) { + m.subtotal = &d } -// ResetDescription resets all changes to the "description" field. -func (m *CategoryMutation) ResetDescription() { - m.description = nil - delete(m.clearedFields, category.FieldDescription) +// Subtotal returns the value of the "subtotal" field in the mutation. +func (m *CartMutation) Subtotal() (r decimal.Decimal, exists bool) { + v := m.subtotal + if v == nil { + return + } + return *v, true } -// AddInternshipIDs adds the "internships" edge to the Internship entity by ids. -func (m *CategoryMutation) AddInternshipIDs(ids ...string) { - if m.internships == nil { - m.internships = make(map[string]struct{}) +// OldSubtotal returns the old "subtotal" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CartMutation) OldSubtotal(ctx context.Context) (v decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSubtotal is only allowed on UpdateOne operations") } - for i := range ids { - m.internships[ids[i]] = struct{}{} + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSubtotal requires an ID field in the mutation") } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSubtotal: %w", err) + } + return oldValue.Subtotal, nil } -// ClearInternships clears the "internships" edge to the Internship entity. -func (m *CategoryMutation) ClearInternships() { - m.clearedinternships = true +// ResetSubtotal resets all changes to the "subtotal" field. +func (m *CartMutation) ResetSubtotal() { + m.subtotal = nil } -// InternshipsCleared reports if the "internships" edge to the Internship entity was cleared. -func (m *CategoryMutation) InternshipsCleared() bool { - return m.clearedinternships +// SetDiscountAmount sets the "discount_amount" field. +func (m *CartMutation) SetDiscountAmount(d decimal.Decimal) { + m.discount_amount = &d } -// RemoveInternshipIDs removes the "internships" edge to the Internship entity by IDs. -func (m *CategoryMutation) RemoveInternshipIDs(ids ...string) { - if m.removedinternships == nil { - m.removedinternships = make(map[string]struct{}) - } - for i := range ids { - delete(m.internships, ids[i]) - m.removedinternships[ids[i]] = struct{}{} +// DiscountAmount returns the value of the "discount_amount" field in the mutation. +func (m *CartMutation) DiscountAmount() (r decimal.Decimal, exists bool) { + v := m.discount_amount + if v == nil { + return } + return *v, true } -// RemovedInternships returns the removed IDs of the "internships" edge to the Internship entity. -func (m *CategoryMutation) RemovedInternshipsIDs() (ids []string) { - for id := range m.removedinternships { - ids = append(ids, id) +// OldDiscountAmount returns the old "discount_amount" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CartMutation) OldDiscountAmount(ctx context.Context) (v decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDiscountAmount is only allowed on UpdateOne operations") } - return + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDiscountAmount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDiscountAmount: %w", err) + } + return oldValue.DiscountAmount, nil } -// InternshipsIDs returns the "internships" edge IDs in the mutation. -func (m *CategoryMutation) InternshipsIDs() (ids []string) { - for id := range m.internships { - ids = append(ids, id) - } - return +// ResetDiscountAmount resets all changes to the "discount_amount" field. +func (m *CartMutation) ResetDiscountAmount() { + m.discount_amount = nil } -// ResetInternships resets all changes to the "internships" edge. -func (m *CategoryMutation) ResetInternships() { - m.internships = nil - m.clearedinternships = false - m.removedinternships = nil +// SetTaxAmount sets the "tax_amount" field. +func (m *CartMutation) SetTaxAmount(d decimal.Decimal) { + m.tax_amount = &d } -// Where appends a list predicates to the CategoryMutation builder. -func (m *CategoryMutation) Where(ps ...predicate.Category) { - m.predicates = append(m.predicates, ps...) +// TaxAmount returns the value of the "tax_amount" field in the mutation. +func (m *CartMutation) TaxAmount() (r decimal.Decimal, exists bool) { + v := m.tax_amount + if v == nil { + return + } + return *v, true } -// WhereP appends storage-level predicates to the CategoryMutation builder. Using this method, -// users can use type-assertion to append predicates that do not depend on any generated package. -func (m *CategoryMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.Category, len(ps)) - for i := range ps { - p[i] = ps[i] +// OldTaxAmount returns the old "tax_amount" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CartMutation) OldTaxAmount(ctx context.Context) (v decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTaxAmount is only allowed on UpdateOne operations") } - m.Where(p...) + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTaxAmount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTaxAmount: %w", err) + } + return oldValue.TaxAmount, nil } -// Op returns the operation name. -func (m *CategoryMutation) Op() Op { - return m.op +// ResetTaxAmount resets all changes to the "tax_amount" field. +func (m *CartMutation) ResetTaxAmount() { + m.tax_amount = nil } -// SetOp allows setting the mutation operation. -func (m *CategoryMutation) SetOp(op Op) { - m.op = op +// SetTotal sets the "total" field. +func (m *CartMutation) SetTotal(d decimal.Decimal) { + m.total = &d } -// Type returns the node type of this mutation (Category). -func (m *CategoryMutation) Type() string { - return m.typ +// Total returns the value of the "total" field in the mutation. +func (m *CartMutation) Total() (r decimal.Decimal, exists bool) { + v := m.total + if v == nil { + return + } + return *v, true } -// Fields returns all fields that were changed during this mutation. Note that in -// order to get all numeric fields that were incremented/decremented, call -// AddedFields(). -func (m *CategoryMutation) Fields() []string { - fields := make([]string, 0, 8) +// OldTotal returns the old "total" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CartMutation) OldTotal(ctx context.Context) (v decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTotal is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTotal requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTotal: %w", err) + } + return oldValue.Total, nil +} + +// ResetTotal resets all changes to the "total" field. +func (m *CartMutation) ResetTotal() { + m.total = nil +} + +// SetExpiresAt sets the "expires_at" field. +func (m *CartMutation) SetExpiresAt(t time.Time) { + m.expires_at = &t +} + +// ExpiresAt returns the value of the "expires_at" field in the mutation. +func (m *CartMutation) ExpiresAt() (r time.Time, exists bool) { + v := m.expires_at + if v == nil { + return + } + return *v, true +} + +// OldExpiresAt returns the old "expires_at" field's value of the Cart entity. +// If the Cart object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CartMutation) OldExpiresAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExpiresAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) + } + return oldValue.ExpiresAt, nil +} + +// ResetExpiresAt resets all changes to the "expires_at" field. +func (m *CartMutation) ResetExpiresAt() { + m.expires_at = nil +} + +// AddLineItemIDs adds the "line_items" edge to the CartLineItems entity by ids. +func (m *CartMutation) AddLineItemIDs(ids ...string) { + if m.line_items == nil { + m.line_items = make(map[string]struct{}) + } + for i := range ids { + m.line_items[ids[i]] = struct{}{} + } +} + +// ClearLineItems clears the "line_items" edge to the CartLineItems entity. +func (m *CartMutation) ClearLineItems() { + m.clearedline_items = true +} + +// LineItemsCleared reports if the "line_items" edge to the CartLineItems entity was cleared. +func (m *CartMutation) LineItemsCleared() bool { + return m.clearedline_items +} + +// RemoveLineItemIDs removes the "line_items" edge to the CartLineItems entity by IDs. +func (m *CartMutation) RemoveLineItemIDs(ids ...string) { + if m.removedline_items == nil { + m.removedline_items = make(map[string]struct{}) + } + for i := range ids { + delete(m.line_items, ids[i]) + m.removedline_items[ids[i]] = struct{}{} + } +} + +// RemovedLineItems returns the removed IDs of the "line_items" edge to the CartLineItems entity. +func (m *CartMutation) RemovedLineItemsIDs() (ids []string) { + for id := range m.removedline_items { + ids = append(ids, id) + } + return +} + +// LineItemsIDs returns the "line_items" edge IDs in the mutation. +func (m *CartMutation) LineItemsIDs() (ids []string) { + for id := range m.line_items { + ids = append(ids, id) + } + return +} + +// ResetLineItems resets all changes to the "line_items" edge. +func (m *CartMutation) ResetLineItems() { + m.line_items = nil + m.clearedline_items = false + m.removedline_items = nil +} + +// ClearUser clears the "user" edge to the User entity. +func (m *CartMutation) ClearUser() { + m.cleareduser = true + m.clearedFields[cart.FieldUserID] = struct{}{} +} + +// UserCleared reports if the "user" edge to the User entity was cleared. +func (m *CartMutation) UserCleared() bool { + return m.cleareduser +} + +// UserIDs returns the "user" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// UserID instead. It exists only for internal usage by the builders. +func (m *CartMutation) UserIDs() (ids []string) { + if id := m.user; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUser resets all changes to the "user" edge. +func (m *CartMutation) ResetUser() { + m.user = nil + m.cleareduser = false +} + +// Where appends a list predicates to the CartMutation builder. +func (m *CartMutation) Where(ps ...predicate.Cart) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the CartMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *CartMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Cart, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *CartMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *CartMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Cart). +func (m *CartMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *CartMutation) Fields() []string { + fields := make([]string, 0, 13) if m.status != nil { - fields = append(fields, category.FieldStatus) + fields = append(fields, cart.FieldStatus) } if m.created_at != nil { - fields = append(fields, category.FieldCreatedAt) + fields = append(fields, cart.FieldCreatedAt) } if m.updated_at != nil { - fields = append(fields, category.FieldUpdatedAt) + fields = append(fields, cart.FieldUpdatedAt) } if m.created_by != nil { - fields = append(fields, category.FieldCreatedBy) + fields = append(fields, cart.FieldCreatedBy) } if m.updated_by != nil { - fields = append(fields, category.FieldUpdatedBy) + fields = append(fields, cart.FieldUpdatedBy) } - if m.name != nil { - fields = append(fields, category.FieldName) + if m.metadata != nil { + fields = append(fields, cart.FieldMetadata) } - if m.lookup_key != nil { - fields = append(fields, category.FieldLookupKey) + if m.user != nil { + fields = append(fields, cart.FieldUserID) } - if m.description != nil { - fields = append(fields, category.FieldDescription) + if m._type != nil { + fields = append(fields, cart.FieldType) + } + if m.subtotal != nil { + fields = append(fields, cart.FieldSubtotal) + } + if m.discount_amount != nil { + fields = append(fields, cart.FieldDiscountAmount) + } + if m.tax_amount != nil { + fields = append(fields, cart.FieldTaxAmount) + } + if m.total != nil { + fields = append(fields, cart.FieldTotal) + } + if m.expires_at != nil { + fields = append(fields, cart.FieldExpiresAt) } return fields } @@ -614,24 +851,34 @@ func (m *CategoryMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *CategoryMutation) Field(name string) (ent.Value, bool) { +func (m *CartMutation) Field(name string) (ent.Value, bool) { switch name { - case category.FieldStatus: + case cart.FieldStatus: return m.Status() - case category.FieldCreatedAt: + case cart.FieldCreatedAt: return m.CreatedAt() - case category.FieldUpdatedAt: + case cart.FieldUpdatedAt: return m.UpdatedAt() - case category.FieldCreatedBy: + case cart.FieldCreatedBy: return m.CreatedBy() - case category.FieldUpdatedBy: + case cart.FieldUpdatedBy: return m.UpdatedBy() - case category.FieldName: - return m.Name() - case category.FieldLookupKey: - return m.LookupKey() - case category.FieldDescription: - return m.Description() + case cart.FieldMetadata: + return m.Metadata() + case cart.FieldUserID: + return m.UserID() + case cart.FieldType: + return m.GetType() + case cart.FieldSubtotal: + return m.Subtotal() + case cart.FieldDiscountAmount: + return m.DiscountAmount() + case cart.FieldTaxAmount: + return m.TaxAmount() + case cart.FieldTotal: + return m.Total() + case cart.FieldExpiresAt: + return m.ExpiresAt() } return nil, false } @@ -639,226 +886,293 @@ func (m *CategoryMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *CategoryMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *CartMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case category.FieldStatus: + case cart.FieldStatus: return m.OldStatus(ctx) - case category.FieldCreatedAt: + case cart.FieldCreatedAt: return m.OldCreatedAt(ctx) - case category.FieldUpdatedAt: + case cart.FieldUpdatedAt: return m.OldUpdatedAt(ctx) - case category.FieldCreatedBy: + case cart.FieldCreatedBy: return m.OldCreatedBy(ctx) - case category.FieldUpdatedBy: + case cart.FieldUpdatedBy: return m.OldUpdatedBy(ctx) - case category.FieldName: - return m.OldName(ctx) - case category.FieldLookupKey: - return m.OldLookupKey(ctx) - case category.FieldDescription: - return m.OldDescription(ctx) - } - return nil, fmt.Errorf("unknown Category field %s", name) + case cart.FieldMetadata: + return m.OldMetadata(ctx) + case cart.FieldUserID: + return m.OldUserID(ctx) + case cart.FieldType: + return m.OldType(ctx) + case cart.FieldSubtotal: + return m.OldSubtotal(ctx) + case cart.FieldDiscountAmount: + return m.OldDiscountAmount(ctx) + case cart.FieldTaxAmount: + return m.OldTaxAmount(ctx) + case cart.FieldTotal: + return m.OldTotal(ctx) + case cart.FieldExpiresAt: + return m.OldExpiresAt(ctx) + } + return nil, fmt.Errorf("unknown Cart field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *CategoryMutation) SetField(name string, value ent.Value) error { +func (m *CartMutation) SetField(name string, value ent.Value) error { switch name { - case category.FieldStatus: + case cart.FieldStatus: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetStatus(v) return nil - case category.FieldCreatedAt: + case cart.FieldCreatedAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedAt(v) return nil - case category.FieldUpdatedAt: + case cart.FieldUpdatedAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedAt(v) return nil - case category.FieldCreatedBy: + case cart.FieldCreatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedBy(v) return nil - case category.FieldUpdatedBy: + case cart.FieldUpdatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedBy(v) return nil - case category.FieldName: - v, ok := value.(string) + case cart.FieldMetadata: + v, ok := value.(map[string]string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetName(v) + m.SetMetadata(v) return nil - case category.FieldLookupKey: + case cart.FieldUserID: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetLookupKey(v) + m.SetUserID(v) return nil - case category.FieldDescription: + case cart.FieldType: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDescription(v) + m.SetType(v) + return nil + case cart.FieldSubtotal: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSubtotal(v) + return nil + case cart.FieldDiscountAmount: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDiscountAmount(v) + return nil + case cart.FieldTaxAmount: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTaxAmount(v) + return nil + case cart.FieldTotal: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTotal(v) + return nil + case cart.FieldExpiresAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExpiresAt(v) return nil } - return fmt.Errorf("unknown Category field %s", name) + return fmt.Errorf("unknown Cart field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *CategoryMutation) AddedFields() []string { +func (m *CartMutation) AddedFields() []string { return nil } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *CategoryMutation) AddedField(name string) (ent.Value, bool) { +func (m *CartMutation) AddedField(name string) (ent.Value, bool) { return nil, false } // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *CategoryMutation) AddField(name string, value ent.Value) error { +func (m *CartMutation) AddField(name string, value ent.Value) error { switch name { } - return fmt.Errorf("unknown Category numeric field %s", name) + return fmt.Errorf("unknown Cart numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *CategoryMutation) ClearedFields() []string { +func (m *CartMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(category.FieldCreatedBy) { - fields = append(fields, category.FieldCreatedBy) + if m.FieldCleared(cart.FieldCreatedBy) { + fields = append(fields, cart.FieldCreatedBy) } - if m.FieldCleared(category.FieldUpdatedBy) { - fields = append(fields, category.FieldUpdatedBy) + if m.FieldCleared(cart.FieldUpdatedBy) { + fields = append(fields, cart.FieldUpdatedBy) } - if m.FieldCleared(category.FieldDescription) { - fields = append(fields, category.FieldDescription) + if m.FieldCleared(cart.FieldMetadata) { + fields = append(fields, cart.FieldMetadata) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *CategoryMutation) FieldCleared(name string) bool { +func (m *CartMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *CategoryMutation) ClearField(name string) error { +func (m *CartMutation) ClearField(name string) error { switch name { - case category.FieldCreatedBy: + case cart.FieldCreatedBy: m.ClearCreatedBy() return nil - case category.FieldUpdatedBy: + case cart.FieldUpdatedBy: m.ClearUpdatedBy() return nil - case category.FieldDescription: - m.ClearDescription() + case cart.FieldMetadata: + m.ClearMetadata() return nil } - return fmt.Errorf("unknown Category nullable field %s", name) + return fmt.Errorf("unknown Cart nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *CategoryMutation) ResetField(name string) error { +func (m *CartMutation) ResetField(name string) error { switch name { - case category.FieldStatus: + case cart.FieldStatus: m.ResetStatus() return nil - case category.FieldCreatedAt: + case cart.FieldCreatedAt: m.ResetCreatedAt() return nil - case category.FieldUpdatedAt: + case cart.FieldUpdatedAt: m.ResetUpdatedAt() return nil - case category.FieldCreatedBy: + case cart.FieldCreatedBy: m.ResetCreatedBy() return nil - case category.FieldUpdatedBy: + case cart.FieldUpdatedBy: m.ResetUpdatedBy() return nil - case category.FieldName: - m.ResetName() + case cart.FieldMetadata: + m.ResetMetadata() return nil - case category.FieldLookupKey: - m.ResetLookupKey() + case cart.FieldUserID: + m.ResetUserID() return nil - case category.FieldDescription: - m.ResetDescription() + case cart.FieldType: + m.ResetType() + return nil + case cart.FieldSubtotal: + m.ResetSubtotal() + return nil + case cart.FieldDiscountAmount: + m.ResetDiscountAmount() + return nil + case cart.FieldTaxAmount: + m.ResetTaxAmount() + return nil + case cart.FieldTotal: + m.ResetTotal() + return nil + case cart.FieldExpiresAt: + m.ResetExpiresAt() return nil } - return fmt.Errorf("unknown Category field %s", name) + return fmt.Errorf("unknown Cart field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *CategoryMutation) AddedEdges() []string { - edges := make([]string, 0, 1) - if m.internships != nil { - edges = append(edges, category.EdgeInternships) +func (m *CartMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.line_items != nil { + edges = append(edges, cart.EdgeLineItems) + } + if m.user != nil { + edges = append(edges, cart.EdgeUser) } return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *CategoryMutation) AddedIDs(name string) []ent.Value { +func (m *CartMutation) AddedIDs(name string) []ent.Value { switch name { - case category.EdgeInternships: - ids := make([]ent.Value, 0, len(m.internships)) - for id := range m.internships { + case cart.EdgeLineItems: + ids := make([]ent.Value, 0, len(m.line_items)) + for id := range m.line_items { ids = append(ids, id) } return ids + case cart.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } } return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *CategoryMutation) RemovedEdges() []string { - edges := make([]string, 0, 1) - if m.removedinternships != nil { - edges = append(edges, category.EdgeInternships) +func (m *CartMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + if m.removedline_items != nil { + edges = append(edges, cart.EdgeLineItems) } return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *CategoryMutation) RemovedIDs(name string) []ent.Value { +func (m *CartMutation) RemovedIDs(name string) []ent.Value { switch name { - case category.EdgeInternships: - ids := make([]ent.Value, 0, len(m.removedinternships)) - for id := range m.removedinternships { + case cart.EdgeLineItems: + ids := make([]ent.Value, 0, len(m.removedline_items)) + for id := range m.removedline_items { ids = append(ids, id) } return ids @@ -867,45 +1181,56 @@ func (m *CategoryMutation) RemovedIDs(name string) []ent.Value { } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *CategoryMutation) ClearedEdges() []string { - edges := make([]string, 0, 1) - if m.clearedinternships { - edges = append(edges, category.EdgeInternships) +func (m *CartMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedline_items { + edges = append(edges, cart.EdgeLineItems) + } + if m.cleareduser { + edges = append(edges, cart.EdgeUser) } return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *CategoryMutation) EdgeCleared(name string) bool { +func (m *CartMutation) EdgeCleared(name string) bool { switch name { - case category.EdgeInternships: - return m.clearedinternships + case cart.EdgeLineItems: + return m.clearedline_items + case cart.EdgeUser: + return m.cleareduser } return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *CategoryMutation) ClearEdge(name string) error { +func (m *CartMutation) ClearEdge(name string) error { switch name { + case cart.EdgeUser: + m.ClearUser() + return nil } - return fmt.Errorf("unknown Category unique edge %s", name) + return fmt.Errorf("unknown Cart unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *CategoryMutation) ResetEdge(name string) error { +func (m *CartMutation) ResetEdge(name string) error { switch name { - case category.EdgeInternships: - m.ResetInternships() + case cart.EdgeLineItems: + m.ResetLineItems() + return nil + case cart.EdgeUser: + m.ResetUser() return nil } - return fmt.Errorf("unknown Category edge %s", name) + return fmt.Errorf("unknown Cart edge %s", name) } -// DiscountMutation represents an operation that mutates the Discount nodes in the graph. -type DiscountMutation struct { +// CartLineItemsMutation represents an operation that mutates the CartLineItems nodes in the graph. +type CartLineItemsMutation struct { config op Op typ string @@ -915,35 +1240,35 @@ type DiscountMutation struct { updated_at *time.Time created_by *string updated_by *string - code *string - description *string - discount_type *types.DiscountType - discount_value *decimal.Decimal - valid_from *time.Time - valid_until *time.Time - is_active *bool - max_uses *int - addmax_uses *int - min_order_value *decimal.Decimal - is_combinable *bool metadata *map[string]string + entity_id *string + entity_type *string + quantity *int + addquantity *int + per_unit_price *decimal.Decimal + tax_amount *decimal.Decimal + discount_amount *decimal.Decimal + subtotal *decimal.Decimal + total *decimal.Decimal clearedFields map[string]struct{} + cart *string + clearedcart bool done bool - oldValue func(context.Context) (*Discount, error) - predicates []predicate.Discount + oldValue func(context.Context) (*CartLineItems, error) + predicates []predicate.CartLineItems } -var _ ent.Mutation = (*DiscountMutation)(nil) +var _ ent.Mutation = (*CartLineItemsMutation)(nil) -// discountOption allows management of the mutation configuration using functional options. -type discountOption func(*DiscountMutation) +// cartlineitemsOption allows management of the mutation configuration using functional options. +type cartlineitemsOption func(*CartLineItemsMutation) -// newDiscountMutation creates new mutation for the Discount entity. -func newDiscountMutation(c config, op Op, opts ...discountOption) *DiscountMutation { - m := &DiscountMutation{ +// newCartLineItemsMutation creates new mutation for the CartLineItems entity. +func newCartLineItemsMutation(c config, op Op, opts ...cartlineitemsOption) *CartLineItemsMutation { + m := &CartLineItemsMutation{ config: c, op: op, - typ: TypeDiscount, + typ: TypeCartLineItems, clearedFields: make(map[string]struct{}), } for _, opt := range opts { @@ -952,20 +1277,20 @@ func newDiscountMutation(c config, op Op, opts ...discountOption) *DiscountMutat return m } -// withDiscountID sets the ID field of the mutation. -func withDiscountID(id string) discountOption { - return func(m *DiscountMutation) { +// withCartLineItemsID sets the ID field of the mutation. +func withCartLineItemsID(id string) cartlineitemsOption { + return func(m *CartLineItemsMutation) { var ( err error once sync.Once - value *Discount + value *CartLineItems ) - m.oldValue = func(ctx context.Context) (*Discount, error) { + m.oldValue = func(ctx context.Context) (*CartLineItems, error) { once.Do(func() { if m.done { err = errors.New("querying old values post mutation is not allowed") } else { - value, err = m.Client().Discount.Get(ctx, id) + value, err = m.Client().CartLineItems.Get(ctx, id) } }) return value, err @@ -974,10 +1299,10 @@ func withDiscountID(id string) discountOption { } } -// withDiscount sets the old Discount of the mutation. -func withDiscount(node *Discount) discountOption { - return func(m *DiscountMutation) { - m.oldValue = func(context.Context) (*Discount, error) { +// withCartLineItems sets the old CartLineItems of the mutation. +func withCartLineItems(node *CartLineItems) cartlineitemsOption { + return func(m *CartLineItemsMutation) { + m.oldValue = func(context.Context) (*CartLineItems, error) { return node, nil } m.id = &node.ID @@ -986,7 +1311,7 @@ func withDiscount(node *Discount) discountOption { // Client returns a new `ent.Client` from the mutation. If the mutation was // executed in a transaction (ent.Tx), a transactional client is returned. -func (m DiscountMutation) Client() *Client { +func (m CartLineItemsMutation) Client() *Client { client := &Client{config: m.config} client.init() return client @@ -994,7 +1319,7 @@ func (m DiscountMutation) Client() *Client { // Tx returns an `ent.Tx` for mutations that were executed in transactions; // it returns an error otherwise. -func (m DiscountMutation) Tx() (*Tx, error) { +func (m CartLineItemsMutation) Tx() (*Tx, error) { if _, ok := m.driver.(*txDriver); !ok { return nil, errors.New("ent: mutation is not running in a transaction") } @@ -1004,14 +1329,14 @@ func (m DiscountMutation) Tx() (*Tx, error) { } // SetID sets the value of the id field. Note that this -// operation is only accepted on creation of Discount entities. -func (m *DiscountMutation) SetID(id string) { +// operation is only accepted on creation of CartLineItems entities. +func (m *CartLineItemsMutation) SetID(id string) { m.id = &id } // ID returns the ID value in the mutation. Note that the ID is only available // if it was provided to the builder or after it was returned from the database. -func (m *DiscountMutation) ID() (id string, exists bool) { +func (m *CartLineItemsMutation) ID() (id string, exists bool) { if m.id == nil { return } @@ -1022,7 +1347,7 @@ func (m *DiscountMutation) ID() (id string, exists bool) { // That means, if the mutation is applied within a transaction with an isolation level such // as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated // or updated by the mutation. -func (m *DiscountMutation) IDs(ctx context.Context) ([]string, error) { +func (m *CartLineItemsMutation) IDs(ctx context.Context) ([]string, error) { switch { case m.op.Is(OpUpdateOne | OpDeleteOne): id, exists := m.ID() @@ -1031,19 +1356,19 @@ func (m *DiscountMutation) IDs(ctx context.Context) ([]string, error) { } fallthrough case m.op.Is(OpUpdate | OpDelete): - return m.Client().Discount.Query().Where(m.predicates...).IDs(ctx) + return m.Client().CartLineItems.Query().Where(m.predicates...).IDs(ctx) default: return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) } } // SetStatus sets the "status" field. -func (m *DiscountMutation) SetStatus(s string) { +func (m *CartLineItemsMutation) SetStatus(s string) { m.status = &s } // Status returns the value of the "status" field in the mutation. -func (m *DiscountMutation) Status() (r string, exists bool) { +func (m *CartLineItemsMutation) Status() (r string, exists bool) { v := m.status if v == nil { return @@ -1051,10 +1376,10 @@ func (m *DiscountMutation) Status() (r string, exists bool) { return *v, true } -// OldStatus returns the old "status" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldStatus returns the old "status" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldStatus(ctx context.Context) (v string, err error) { +func (m *CartLineItemsMutation) OldStatus(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldStatus is only allowed on UpdateOne operations") } @@ -1069,17 +1394,17 @@ func (m *DiscountMutation) OldStatus(ctx context.Context) (v string, err error) } // ResetStatus resets all changes to the "status" field. -func (m *DiscountMutation) ResetStatus() { +func (m *CartLineItemsMutation) ResetStatus() { m.status = nil } // SetCreatedAt sets the "created_at" field. -func (m *DiscountMutation) SetCreatedAt(t time.Time) { +func (m *CartLineItemsMutation) SetCreatedAt(t time.Time) { m.created_at = &t } // CreatedAt returns the value of the "created_at" field in the mutation. -func (m *DiscountMutation) CreatedAt() (r time.Time, exists bool) { +func (m *CartLineItemsMutation) CreatedAt() (r time.Time, exists bool) { v := m.created_at if v == nil { return @@ -1087,10 +1412,10 @@ func (m *DiscountMutation) CreatedAt() (r time.Time, exists bool) { return *v, true } -// OldCreatedAt returns the old "created_at" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedAt returns the old "created_at" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { +func (m *CartLineItemsMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") } @@ -1105,17 +1430,17 @@ func (m *DiscountMutation) OldCreatedAt(ctx context.Context) (v time.Time, err e } // ResetCreatedAt resets all changes to the "created_at" field. -func (m *DiscountMutation) ResetCreatedAt() { +func (m *CartLineItemsMutation) ResetCreatedAt() { m.created_at = nil } // SetUpdatedAt sets the "updated_at" field. -func (m *DiscountMutation) SetUpdatedAt(t time.Time) { +func (m *CartLineItemsMutation) SetUpdatedAt(t time.Time) { m.updated_at = &t } // UpdatedAt returns the value of the "updated_at" field in the mutation. -func (m *DiscountMutation) UpdatedAt() (r time.Time, exists bool) { +func (m *CartLineItemsMutation) UpdatedAt() (r time.Time, exists bool) { v := m.updated_at if v == nil { return @@ -1123,10 +1448,10 @@ func (m *DiscountMutation) UpdatedAt() (r time.Time, exists bool) { return *v, true } -// OldUpdatedAt returns the old "updated_at" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedAt returns the old "updated_at" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { +func (m *CartLineItemsMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") } @@ -1141,17 +1466,17 @@ func (m *DiscountMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err e } // ResetUpdatedAt resets all changes to the "updated_at" field. -func (m *DiscountMutation) ResetUpdatedAt() { +func (m *CartLineItemsMutation) ResetUpdatedAt() { m.updated_at = nil } // SetCreatedBy sets the "created_by" field. -func (m *DiscountMutation) SetCreatedBy(s string) { +func (m *CartLineItemsMutation) SetCreatedBy(s string) { m.created_by = &s } // CreatedBy returns the value of the "created_by" field in the mutation. -func (m *DiscountMutation) CreatedBy() (r string, exists bool) { +func (m *CartLineItemsMutation) CreatedBy() (r string, exists bool) { v := m.created_by if v == nil { return @@ -1159,10 +1484,10 @@ func (m *DiscountMutation) CreatedBy() (r string, exists bool) { return *v, true } -// OldCreatedBy returns the old "created_by" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedBy returns the old "created_by" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldCreatedBy(ctx context.Context) (v string, err error) { +func (m *CartLineItemsMutation) OldCreatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") } @@ -1177,30 +1502,30 @@ func (m *DiscountMutation) OldCreatedBy(ctx context.Context) (v string, err erro } // ClearCreatedBy clears the value of the "created_by" field. -func (m *DiscountMutation) ClearCreatedBy() { +func (m *CartLineItemsMutation) ClearCreatedBy() { m.created_by = nil - m.clearedFields[discount.FieldCreatedBy] = struct{}{} + m.clearedFields[cartlineitems.FieldCreatedBy] = struct{}{} } // CreatedByCleared returns if the "created_by" field was cleared in this mutation. -func (m *DiscountMutation) CreatedByCleared() bool { - _, ok := m.clearedFields[discount.FieldCreatedBy] +func (m *CartLineItemsMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[cartlineitems.FieldCreatedBy] return ok } // ResetCreatedBy resets all changes to the "created_by" field. -func (m *DiscountMutation) ResetCreatedBy() { +func (m *CartLineItemsMutation) ResetCreatedBy() { m.created_by = nil - delete(m.clearedFields, discount.FieldCreatedBy) + delete(m.clearedFields, cartlineitems.FieldCreatedBy) } // SetUpdatedBy sets the "updated_by" field. -func (m *DiscountMutation) SetUpdatedBy(s string) { +func (m *CartLineItemsMutation) SetUpdatedBy(s string) { m.updated_by = &s } // UpdatedBy returns the value of the "updated_by" field in the mutation. -func (m *DiscountMutation) UpdatedBy() (r string, exists bool) { +func (m *CartLineItemsMutation) UpdatedBy() (r string, exists bool) { v := m.updated_by if v == nil { return @@ -1208,10 +1533,10 @@ func (m *DiscountMutation) UpdatedBy() (r string, exists bool) { return *v, true } -// OldUpdatedBy returns the old "updated_by" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedBy returns the old "updated_by" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { +func (m *CartLineItemsMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") } @@ -1226,514 +1551,452 @@ func (m *DiscountMutation) OldUpdatedBy(ctx context.Context) (v string, err erro } // ClearUpdatedBy clears the value of the "updated_by" field. -func (m *DiscountMutation) ClearUpdatedBy() { +func (m *CartLineItemsMutation) ClearUpdatedBy() { m.updated_by = nil - m.clearedFields[discount.FieldUpdatedBy] = struct{}{} + m.clearedFields[cartlineitems.FieldUpdatedBy] = struct{}{} } // UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. -func (m *DiscountMutation) UpdatedByCleared() bool { - _, ok := m.clearedFields[discount.FieldUpdatedBy] +func (m *CartLineItemsMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[cartlineitems.FieldUpdatedBy] return ok } // ResetUpdatedBy resets all changes to the "updated_by" field. -func (m *DiscountMutation) ResetUpdatedBy() { +func (m *CartLineItemsMutation) ResetUpdatedBy() { m.updated_by = nil - delete(m.clearedFields, discount.FieldUpdatedBy) + delete(m.clearedFields, cartlineitems.FieldUpdatedBy) } -// SetCode sets the "code" field. -func (m *DiscountMutation) SetCode(s string) { - m.code = &s +// SetMetadata sets the "metadata" field. +func (m *CartLineItemsMutation) SetMetadata(value map[string]string) { + m.metadata = &value } -// Code returns the value of the "code" field in the mutation. -func (m *DiscountMutation) Code() (r string, exists bool) { - v := m.code +// Metadata returns the value of the "metadata" field in the mutation. +func (m *CartLineItemsMutation) Metadata() (r map[string]string, exists bool) { + v := m.metadata if v == nil { return } return *v, true } -// OldCode returns the old "code" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldMetadata returns the old "metadata" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldCode(ctx context.Context) (v string, err error) { +func (m *CartLineItemsMutation) OldMetadata(ctx context.Context) (v map[string]string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCode is only allowed on UpdateOne operations") + return v, errors.New("OldMetadata is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCode requires an ID field in the mutation") + return v, errors.New("OldMetadata requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCode: %w", err) + return v, fmt.Errorf("querying old value for OldMetadata: %w", err) } - return oldValue.Code, nil + return oldValue.Metadata, nil } -// ResetCode resets all changes to the "code" field. -func (m *DiscountMutation) ResetCode() { - m.code = nil +// ClearMetadata clears the value of the "metadata" field. +func (m *CartLineItemsMutation) ClearMetadata() { + m.metadata = nil + m.clearedFields[cartlineitems.FieldMetadata] = struct{}{} } -// SetDescription sets the "description" field. -func (m *DiscountMutation) SetDescription(s string) { - m.description = &s +// MetadataCleared returns if the "metadata" field was cleared in this mutation. +func (m *CartLineItemsMutation) MetadataCleared() bool { + _, ok := m.clearedFields[cartlineitems.FieldMetadata] + return ok } -// Description returns the value of the "description" field in the mutation. -func (m *DiscountMutation) Description() (r string, exists bool) { - v := m.description +// ResetMetadata resets all changes to the "metadata" field. +func (m *CartLineItemsMutation) ResetMetadata() { + m.metadata = nil + delete(m.clearedFields, cartlineitems.FieldMetadata) +} + +// SetCartID sets the "cart_id" field. +func (m *CartLineItemsMutation) SetCartID(s string) { + m.cart = &s +} + +// CartID returns the value of the "cart_id" field in the mutation. +func (m *CartLineItemsMutation) CartID() (r string, exists bool) { + v := m.cart if v == nil { return } return *v, true } -// OldDescription returns the old "description" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldCartID returns the old "cart_id" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldDescription(ctx context.Context) (v string, err error) { +func (m *CartLineItemsMutation) OldCartID(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldDescription is only allowed on UpdateOne operations") + return v, errors.New("OldCartID is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldDescription requires an ID field in the mutation") + return v, errors.New("OldCartID requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldDescription: %w", err) + return v, fmt.Errorf("querying old value for OldCartID: %w", err) } - return oldValue.Description, nil -} - -// ClearDescription clears the value of the "description" field. -func (m *DiscountMutation) ClearDescription() { - m.description = nil - m.clearedFields[discount.FieldDescription] = struct{}{} -} - -// DescriptionCleared returns if the "description" field was cleared in this mutation. -func (m *DiscountMutation) DescriptionCleared() bool { - _, ok := m.clearedFields[discount.FieldDescription] - return ok + return oldValue.CartID, nil } -// ResetDescription resets all changes to the "description" field. -func (m *DiscountMutation) ResetDescription() { - m.description = nil - delete(m.clearedFields, discount.FieldDescription) +// ResetCartID resets all changes to the "cart_id" field. +func (m *CartLineItemsMutation) ResetCartID() { + m.cart = nil } -// SetDiscountType sets the "discount_type" field. -func (m *DiscountMutation) SetDiscountType(tt types.DiscountType) { - m.discount_type = &tt +// SetEntityID sets the "entity_id" field. +func (m *CartLineItemsMutation) SetEntityID(s string) { + m.entity_id = &s } -// DiscountType returns the value of the "discount_type" field in the mutation. -func (m *DiscountMutation) DiscountType() (r types.DiscountType, exists bool) { - v := m.discount_type +// EntityID returns the value of the "entity_id" field in the mutation. +func (m *CartLineItemsMutation) EntityID() (r string, exists bool) { + v := m.entity_id if v == nil { return } return *v, true } -// OldDiscountType returns the old "discount_type" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldEntityID returns the old "entity_id" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldDiscountType(ctx context.Context) (v types.DiscountType, err error) { +func (m *CartLineItemsMutation) OldEntityID(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldDiscountType is only allowed on UpdateOne operations") + return v, errors.New("OldEntityID is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldDiscountType requires an ID field in the mutation") + return v, errors.New("OldEntityID requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldDiscountType: %w", err) + return v, fmt.Errorf("querying old value for OldEntityID: %w", err) } - return oldValue.DiscountType, nil + return oldValue.EntityID, nil } -// ResetDiscountType resets all changes to the "discount_type" field. -func (m *DiscountMutation) ResetDiscountType() { - m.discount_type = nil +// ResetEntityID resets all changes to the "entity_id" field. +func (m *CartLineItemsMutation) ResetEntityID() { + m.entity_id = nil } -// SetDiscountValue sets the "discount_value" field. -func (m *DiscountMutation) SetDiscountValue(d decimal.Decimal) { - m.discount_value = &d +// SetEntityType sets the "entity_type" field. +func (m *CartLineItemsMutation) SetEntityType(s string) { + m.entity_type = &s } -// DiscountValue returns the value of the "discount_value" field in the mutation. -func (m *DiscountMutation) DiscountValue() (r decimal.Decimal, exists bool) { - v := m.discount_value +// EntityType returns the value of the "entity_type" field in the mutation. +func (m *CartLineItemsMutation) EntityType() (r string, exists bool) { + v := m.entity_type if v == nil { return } return *v, true } -// OldDiscountValue returns the old "discount_value" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldEntityType returns the old "entity_type" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldDiscountValue(ctx context.Context) (v decimal.Decimal, err error) { +func (m *CartLineItemsMutation) OldEntityType(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldDiscountValue is only allowed on UpdateOne operations") + return v, errors.New("OldEntityType is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldDiscountValue requires an ID field in the mutation") + return v, errors.New("OldEntityType requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldDiscountValue: %w", err) + return v, fmt.Errorf("querying old value for OldEntityType: %w", err) } - return oldValue.DiscountValue, nil + return oldValue.EntityType, nil } -// ResetDiscountValue resets all changes to the "discount_value" field. -func (m *DiscountMutation) ResetDiscountValue() { - m.discount_value = nil +// ResetEntityType resets all changes to the "entity_type" field. +func (m *CartLineItemsMutation) ResetEntityType() { + m.entity_type = nil } -// SetValidFrom sets the "valid_from" field. -func (m *DiscountMutation) SetValidFrom(t time.Time) { - m.valid_from = &t +// SetQuantity sets the "quantity" field. +func (m *CartLineItemsMutation) SetQuantity(i int) { + m.quantity = &i + m.addquantity = nil } -// ValidFrom returns the value of the "valid_from" field in the mutation. -func (m *DiscountMutation) ValidFrom() (r time.Time, exists bool) { - v := m.valid_from +// Quantity returns the value of the "quantity" field in the mutation. +func (m *CartLineItemsMutation) Quantity() (r int, exists bool) { + v := m.quantity if v == nil { return } return *v, true } -// OldValidFrom returns the old "valid_from" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldQuantity returns the old "quantity" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldValidFrom(ctx context.Context) (v time.Time, err error) { +func (m *CartLineItemsMutation) OldQuantity(ctx context.Context) (v int, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldValidFrom is only allowed on UpdateOne operations") + return v, errors.New("OldQuantity is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldValidFrom requires an ID field in the mutation") + return v, errors.New("OldQuantity requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldValidFrom: %w", err) + return v, fmt.Errorf("querying old value for OldQuantity: %w", err) } - return oldValue.ValidFrom, nil + return oldValue.Quantity, nil } -// ResetValidFrom resets all changes to the "valid_from" field. -func (m *DiscountMutation) ResetValidFrom() { - m.valid_from = nil +// AddQuantity adds i to the "quantity" field. +func (m *CartLineItemsMutation) AddQuantity(i int) { + if m.addquantity != nil { + *m.addquantity += i + } else { + m.addquantity = &i + } } -// SetValidUntil sets the "valid_until" field. -func (m *DiscountMutation) SetValidUntil(t time.Time) { - m.valid_until = &t +// AddedQuantity returns the value that was added to the "quantity" field in this mutation. +func (m *CartLineItemsMutation) AddedQuantity() (r int, exists bool) { + v := m.addquantity + if v == nil { + return + } + return *v, true } -// ValidUntil returns the value of the "valid_until" field in the mutation. -func (m *DiscountMutation) ValidUntil() (r time.Time, exists bool) { - v := m.valid_until +// ResetQuantity resets all changes to the "quantity" field. +func (m *CartLineItemsMutation) ResetQuantity() { + m.quantity = nil + m.addquantity = nil +} + +// SetPerUnitPrice sets the "per_unit_price" field. +func (m *CartLineItemsMutation) SetPerUnitPrice(d decimal.Decimal) { + m.per_unit_price = &d +} + +// PerUnitPrice returns the value of the "per_unit_price" field in the mutation. +func (m *CartLineItemsMutation) PerUnitPrice() (r decimal.Decimal, exists bool) { + v := m.per_unit_price if v == nil { return } return *v, true } -// OldValidUntil returns the old "valid_until" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldPerUnitPrice returns the old "per_unit_price" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldValidUntil(ctx context.Context) (v *time.Time, err error) { +func (m *CartLineItemsMutation) OldPerUnitPrice(ctx context.Context) (v decimal.Decimal, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldValidUntil is only allowed on UpdateOne operations") + return v, errors.New("OldPerUnitPrice is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldValidUntil requires an ID field in the mutation") + return v, errors.New("OldPerUnitPrice requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldValidUntil: %w", err) + return v, fmt.Errorf("querying old value for OldPerUnitPrice: %w", err) } - return oldValue.ValidUntil, nil -} - -// ClearValidUntil clears the value of the "valid_until" field. -func (m *DiscountMutation) ClearValidUntil() { - m.valid_until = nil - m.clearedFields[discount.FieldValidUntil] = struct{}{} -} - -// ValidUntilCleared returns if the "valid_until" field was cleared in this mutation. -func (m *DiscountMutation) ValidUntilCleared() bool { - _, ok := m.clearedFields[discount.FieldValidUntil] - return ok + return oldValue.PerUnitPrice, nil } -// ResetValidUntil resets all changes to the "valid_until" field. -func (m *DiscountMutation) ResetValidUntil() { - m.valid_until = nil - delete(m.clearedFields, discount.FieldValidUntil) +// ResetPerUnitPrice resets all changes to the "per_unit_price" field. +func (m *CartLineItemsMutation) ResetPerUnitPrice() { + m.per_unit_price = nil } -// SetIsActive sets the "is_active" field. -func (m *DiscountMutation) SetIsActive(b bool) { - m.is_active = &b +// SetTaxAmount sets the "tax_amount" field. +func (m *CartLineItemsMutation) SetTaxAmount(d decimal.Decimal) { + m.tax_amount = &d } -// IsActive returns the value of the "is_active" field in the mutation. -func (m *DiscountMutation) IsActive() (r bool, exists bool) { - v := m.is_active +// TaxAmount returns the value of the "tax_amount" field in the mutation. +func (m *CartLineItemsMutation) TaxAmount() (r decimal.Decimal, exists bool) { + v := m.tax_amount if v == nil { return } return *v, true } -// OldIsActive returns the old "is_active" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldTaxAmount returns the old "tax_amount" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldIsActive(ctx context.Context) (v bool, err error) { +func (m *CartLineItemsMutation) OldTaxAmount(ctx context.Context) (v decimal.Decimal, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldIsActive is only allowed on UpdateOne operations") + return v, errors.New("OldTaxAmount is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldIsActive requires an ID field in the mutation") + return v, errors.New("OldTaxAmount requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldIsActive: %w", err) + return v, fmt.Errorf("querying old value for OldTaxAmount: %w", err) } - return oldValue.IsActive, nil + return oldValue.TaxAmount, nil } -// ResetIsActive resets all changes to the "is_active" field. -func (m *DiscountMutation) ResetIsActive() { - m.is_active = nil +// ResetTaxAmount resets all changes to the "tax_amount" field. +func (m *CartLineItemsMutation) ResetTaxAmount() { + m.tax_amount = nil } -// SetMaxUses sets the "max_uses" field. -func (m *DiscountMutation) SetMaxUses(i int) { - m.max_uses = &i - m.addmax_uses = nil +// SetDiscountAmount sets the "discount_amount" field. +func (m *CartLineItemsMutation) SetDiscountAmount(d decimal.Decimal) { + m.discount_amount = &d } -// MaxUses returns the value of the "max_uses" field in the mutation. -func (m *DiscountMutation) MaxUses() (r int, exists bool) { - v := m.max_uses +// DiscountAmount returns the value of the "discount_amount" field in the mutation. +func (m *CartLineItemsMutation) DiscountAmount() (r decimal.Decimal, exists bool) { + v := m.discount_amount if v == nil { return } return *v, true } -// OldMaxUses returns the old "max_uses" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldDiscountAmount returns the old "discount_amount" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldMaxUses(ctx context.Context) (v *int, err error) { +func (m *CartLineItemsMutation) OldDiscountAmount(ctx context.Context) (v decimal.Decimal, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldMaxUses is only allowed on UpdateOne operations") + return v, errors.New("OldDiscountAmount is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldMaxUses requires an ID field in the mutation") + return v, errors.New("OldDiscountAmount requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldMaxUses: %w", err) + return v, fmt.Errorf("querying old value for OldDiscountAmount: %w", err) } - return oldValue.MaxUses, nil -} - -// AddMaxUses adds i to the "max_uses" field. -func (m *DiscountMutation) AddMaxUses(i int) { - if m.addmax_uses != nil { - *m.addmax_uses += i - } else { - m.addmax_uses = &i - } -} - -// AddedMaxUses returns the value that was added to the "max_uses" field in this mutation. -func (m *DiscountMutation) AddedMaxUses() (r int, exists bool) { - v := m.addmax_uses - if v == nil { - return - } - return *v, true -} - -// ClearMaxUses clears the value of the "max_uses" field. -func (m *DiscountMutation) ClearMaxUses() { - m.max_uses = nil - m.addmax_uses = nil - m.clearedFields[discount.FieldMaxUses] = struct{}{} -} - -// MaxUsesCleared returns if the "max_uses" field was cleared in this mutation. -func (m *DiscountMutation) MaxUsesCleared() bool { - _, ok := m.clearedFields[discount.FieldMaxUses] - return ok + return oldValue.DiscountAmount, nil } -// ResetMaxUses resets all changes to the "max_uses" field. -func (m *DiscountMutation) ResetMaxUses() { - m.max_uses = nil - m.addmax_uses = nil - delete(m.clearedFields, discount.FieldMaxUses) +// ResetDiscountAmount resets all changes to the "discount_amount" field. +func (m *CartLineItemsMutation) ResetDiscountAmount() { + m.discount_amount = nil } -// SetMinOrderValue sets the "min_order_value" field. -func (m *DiscountMutation) SetMinOrderValue(d decimal.Decimal) { - m.min_order_value = &d +// SetSubtotal sets the "subtotal" field. +func (m *CartLineItemsMutation) SetSubtotal(d decimal.Decimal) { + m.subtotal = &d } -// MinOrderValue returns the value of the "min_order_value" field in the mutation. -func (m *DiscountMutation) MinOrderValue() (r decimal.Decimal, exists bool) { - v := m.min_order_value +// Subtotal returns the value of the "subtotal" field in the mutation. +func (m *CartLineItemsMutation) Subtotal() (r decimal.Decimal, exists bool) { + v := m.subtotal if v == nil { return } return *v, true } -// OldMinOrderValue returns the old "min_order_value" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldSubtotal returns the old "subtotal" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldMinOrderValue(ctx context.Context) (v *decimal.Decimal, err error) { +func (m *CartLineItemsMutation) OldSubtotal(ctx context.Context) (v decimal.Decimal, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldMinOrderValue is only allowed on UpdateOne operations") + return v, errors.New("OldSubtotal is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldMinOrderValue requires an ID field in the mutation") + return v, errors.New("OldSubtotal requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldMinOrderValue: %w", err) + return v, fmt.Errorf("querying old value for OldSubtotal: %w", err) } - return oldValue.MinOrderValue, nil -} - -// ClearMinOrderValue clears the value of the "min_order_value" field. -func (m *DiscountMutation) ClearMinOrderValue() { - m.min_order_value = nil - m.clearedFields[discount.FieldMinOrderValue] = struct{}{} + return oldValue.Subtotal, nil } -// MinOrderValueCleared returns if the "min_order_value" field was cleared in this mutation. -func (m *DiscountMutation) MinOrderValueCleared() bool { - _, ok := m.clearedFields[discount.FieldMinOrderValue] - return ok -} - -// ResetMinOrderValue resets all changes to the "min_order_value" field. -func (m *DiscountMutation) ResetMinOrderValue() { - m.min_order_value = nil - delete(m.clearedFields, discount.FieldMinOrderValue) +// ResetSubtotal resets all changes to the "subtotal" field. +func (m *CartLineItemsMutation) ResetSubtotal() { + m.subtotal = nil } -// SetIsCombinable sets the "is_combinable" field. -func (m *DiscountMutation) SetIsCombinable(b bool) { - m.is_combinable = &b +// SetTotal sets the "total" field. +func (m *CartLineItemsMutation) SetTotal(d decimal.Decimal) { + m.total = &d } -// IsCombinable returns the value of the "is_combinable" field in the mutation. -func (m *DiscountMutation) IsCombinable() (r bool, exists bool) { - v := m.is_combinable +// Total returns the value of the "total" field in the mutation. +func (m *CartLineItemsMutation) Total() (r decimal.Decimal, exists bool) { + v := m.total if v == nil { return } return *v, true } -// OldIsCombinable returns the old "is_combinable" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. +// OldTotal returns the old "total" field's value of the CartLineItems entity. +// If the CartLineItems object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldIsCombinable(ctx context.Context) (v bool, err error) { +func (m *CartLineItemsMutation) OldTotal(ctx context.Context) (v decimal.Decimal, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldIsCombinable is only allowed on UpdateOne operations") + return v, errors.New("OldTotal is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldIsCombinable requires an ID field in the mutation") + return v, errors.New("OldTotal requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldIsCombinable: %w", err) + return v, fmt.Errorf("querying old value for OldTotal: %w", err) } - return oldValue.IsCombinable, nil + return oldValue.Total, nil } -// ResetIsCombinable resets all changes to the "is_combinable" field. -func (m *DiscountMutation) ResetIsCombinable() { - m.is_combinable = nil +// ResetTotal resets all changes to the "total" field. +func (m *CartLineItemsMutation) ResetTotal() { + m.total = nil } -// SetMetadata sets the "metadata" field. -func (m *DiscountMutation) SetMetadata(value map[string]string) { - m.metadata = &value +// ClearCart clears the "cart" edge to the Cart entity. +func (m *CartLineItemsMutation) ClearCart() { + m.clearedcart = true + m.clearedFields[cartlineitems.FieldCartID] = struct{}{} } -// Metadata returns the value of the "metadata" field in the mutation. -func (m *DiscountMutation) Metadata() (r map[string]string, exists bool) { - v := m.metadata - if v == nil { - return - } - return *v, true +// CartCleared reports if the "cart" edge to the Cart entity was cleared. +func (m *CartLineItemsMutation) CartCleared() bool { + return m.clearedcart } -// OldMetadata returns the old "metadata" field's value of the Discount entity. -// If the Discount object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *DiscountMutation) OldMetadata(ctx context.Context) (v map[string]string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldMetadata is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldMetadata requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldMetadata: %w", err) +// CartIDs returns the "cart" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// CartID instead. It exists only for internal usage by the builders. +func (m *CartLineItemsMutation) CartIDs() (ids []string) { + if id := m.cart; id != nil { + ids = append(ids, *id) } - return oldValue.Metadata, nil -} - -// ClearMetadata clears the value of the "metadata" field. -func (m *DiscountMutation) ClearMetadata() { - m.metadata = nil - m.clearedFields[discount.FieldMetadata] = struct{}{} -} - -// MetadataCleared returns if the "metadata" field was cleared in this mutation. -func (m *DiscountMutation) MetadataCleared() bool { - _, ok := m.clearedFields[discount.FieldMetadata] - return ok + return } -// ResetMetadata resets all changes to the "metadata" field. -func (m *DiscountMutation) ResetMetadata() { - m.metadata = nil - delete(m.clearedFields, discount.FieldMetadata) +// ResetCart resets all changes to the "cart" edge. +func (m *CartLineItemsMutation) ResetCart() { + m.cart = nil + m.clearedcart = false } -// Where appends a list predicates to the DiscountMutation builder. -func (m *DiscountMutation) Where(ps ...predicate.Discount) { +// Where appends a list predicates to the CartLineItemsMutation builder. +func (m *CartLineItemsMutation) Where(ps ...predicate.CartLineItems) { m.predicates = append(m.predicates, ps...) } -// WhereP appends storage-level predicates to the DiscountMutation builder. Using this method, +// WhereP appends storage-level predicates to the CartLineItemsMutation builder. Using this method, // users can use type-assertion to append predicates that do not depend on any generated package. -func (m *DiscountMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.Discount, len(ps)) +func (m *CartLineItemsMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.CartLineItems, len(ps)) for i := range ps { p[i] = ps[i] } @@ -1741,72 +2004,69 @@ func (m *DiscountMutation) WhereP(ps ...func(*sql.Selector)) { } // Op returns the operation name. -func (m *DiscountMutation) Op() Op { +func (m *CartLineItemsMutation) Op() Op { return m.op } // SetOp allows setting the mutation operation. -func (m *DiscountMutation) SetOp(op Op) { +func (m *CartLineItemsMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (Discount). -func (m *DiscountMutation) Type() string { +// Type returns the node type of this mutation (CartLineItems). +func (m *CartLineItemsMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *DiscountMutation) Fields() []string { - fields := make([]string, 0, 16) +func (m *CartLineItemsMutation) Fields() []string { + fields := make([]string, 0, 15) if m.status != nil { - fields = append(fields, discount.FieldStatus) + fields = append(fields, cartlineitems.FieldStatus) } if m.created_at != nil { - fields = append(fields, discount.FieldCreatedAt) + fields = append(fields, cartlineitems.FieldCreatedAt) } if m.updated_at != nil { - fields = append(fields, discount.FieldUpdatedAt) + fields = append(fields, cartlineitems.FieldUpdatedAt) } if m.created_by != nil { - fields = append(fields, discount.FieldCreatedBy) + fields = append(fields, cartlineitems.FieldCreatedBy) } if m.updated_by != nil { - fields = append(fields, discount.FieldUpdatedBy) - } - if m.code != nil { - fields = append(fields, discount.FieldCode) + fields = append(fields, cartlineitems.FieldUpdatedBy) } - if m.description != nil { - fields = append(fields, discount.FieldDescription) + if m.metadata != nil { + fields = append(fields, cartlineitems.FieldMetadata) } - if m.discount_type != nil { - fields = append(fields, discount.FieldDiscountType) + if m.cart != nil { + fields = append(fields, cartlineitems.FieldCartID) } - if m.discount_value != nil { - fields = append(fields, discount.FieldDiscountValue) + if m.entity_id != nil { + fields = append(fields, cartlineitems.FieldEntityID) } - if m.valid_from != nil { - fields = append(fields, discount.FieldValidFrom) + if m.entity_type != nil { + fields = append(fields, cartlineitems.FieldEntityType) } - if m.valid_until != nil { - fields = append(fields, discount.FieldValidUntil) + if m.quantity != nil { + fields = append(fields, cartlineitems.FieldQuantity) } - if m.is_active != nil { - fields = append(fields, discount.FieldIsActive) + if m.per_unit_price != nil { + fields = append(fields, cartlineitems.FieldPerUnitPrice) } - if m.max_uses != nil { - fields = append(fields, discount.FieldMaxUses) + if m.tax_amount != nil { + fields = append(fields, cartlineitems.FieldTaxAmount) } - if m.min_order_value != nil { - fields = append(fields, discount.FieldMinOrderValue) + if m.discount_amount != nil { + fields = append(fields, cartlineitems.FieldDiscountAmount) } - if m.is_combinable != nil { - fields = append(fields, discount.FieldIsCombinable) + if m.subtotal != nil { + fields = append(fields, cartlineitems.FieldSubtotal) } - if m.metadata != nil { - fields = append(fields, discount.FieldMetadata) + if m.total != nil { + fields = append(fields, cartlineitems.FieldTotal) } return fields } @@ -1814,40 +2074,38 @@ func (m *DiscountMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *DiscountMutation) Field(name string) (ent.Value, bool) { +func (m *CartLineItemsMutation) Field(name string) (ent.Value, bool) { switch name { - case discount.FieldStatus: + case cartlineitems.FieldStatus: return m.Status() - case discount.FieldCreatedAt: + case cartlineitems.FieldCreatedAt: return m.CreatedAt() - case discount.FieldUpdatedAt: + case cartlineitems.FieldUpdatedAt: return m.UpdatedAt() - case discount.FieldCreatedBy: + case cartlineitems.FieldCreatedBy: return m.CreatedBy() - case discount.FieldUpdatedBy: + case cartlineitems.FieldUpdatedBy: return m.UpdatedBy() - case discount.FieldCode: - return m.Code() - case discount.FieldDescription: - return m.Description() - case discount.FieldDiscountType: - return m.DiscountType() - case discount.FieldDiscountValue: - return m.DiscountValue() - case discount.FieldValidFrom: - return m.ValidFrom() - case discount.FieldValidUntil: - return m.ValidUntil() - case discount.FieldIsActive: - return m.IsActive() - case discount.FieldMaxUses: - return m.MaxUses() - case discount.FieldMinOrderValue: - return m.MinOrderValue() - case discount.FieldIsCombinable: - return m.IsCombinable() - case discount.FieldMetadata: + case cartlineitems.FieldMetadata: return m.Metadata() + case cartlineitems.FieldCartID: + return m.CartID() + case cartlineitems.FieldEntityID: + return m.EntityID() + case cartlineitems.FieldEntityType: + return m.EntityType() + case cartlineitems.FieldQuantity: + return m.Quantity() + case cartlineitems.FieldPerUnitPrice: + return m.PerUnitPrice() + case cartlineitems.FieldTaxAmount: + return m.TaxAmount() + case cartlineitems.FieldDiscountAmount: + return m.DiscountAmount() + case cartlineitems.FieldSubtotal: + return m.Subtotal() + case cartlineitems.FieldTotal: + return m.Total() } return nil, false } @@ -1855,171 +2113,162 @@ func (m *DiscountMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *DiscountMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *CartLineItemsMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case discount.FieldStatus: + case cartlineitems.FieldStatus: return m.OldStatus(ctx) - case discount.FieldCreatedAt: + case cartlineitems.FieldCreatedAt: return m.OldCreatedAt(ctx) - case discount.FieldUpdatedAt: + case cartlineitems.FieldUpdatedAt: return m.OldUpdatedAt(ctx) - case discount.FieldCreatedBy: + case cartlineitems.FieldCreatedBy: return m.OldCreatedBy(ctx) - case discount.FieldUpdatedBy: + case cartlineitems.FieldUpdatedBy: return m.OldUpdatedBy(ctx) - case discount.FieldCode: - return m.OldCode(ctx) - case discount.FieldDescription: - return m.OldDescription(ctx) - case discount.FieldDiscountType: - return m.OldDiscountType(ctx) - case discount.FieldDiscountValue: - return m.OldDiscountValue(ctx) - case discount.FieldValidFrom: - return m.OldValidFrom(ctx) - case discount.FieldValidUntil: - return m.OldValidUntil(ctx) - case discount.FieldIsActive: - return m.OldIsActive(ctx) - case discount.FieldMaxUses: - return m.OldMaxUses(ctx) - case discount.FieldMinOrderValue: - return m.OldMinOrderValue(ctx) - case discount.FieldIsCombinable: - return m.OldIsCombinable(ctx) - case discount.FieldMetadata: + case cartlineitems.FieldMetadata: return m.OldMetadata(ctx) - } - return nil, fmt.Errorf("unknown Discount field %s", name) + case cartlineitems.FieldCartID: + return m.OldCartID(ctx) + case cartlineitems.FieldEntityID: + return m.OldEntityID(ctx) + case cartlineitems.FieldEntityType: + return m.OldEntityType(ctx) + case cartlineitems.FieldQuantity: + return m.OldQuantity(ctx) + case cartlineitems.FieldPerUnitPrice: + return m.OldPerUnitPrice(ctx) + case cartlineitems.FieldTaxAmount: + return m.OldTaxAmount(ctx) + case cartlineitems.FieldDiscountAmount: + return m.OldDiscountAmount(ctx) + case cartlineitems.FieldSubtotal: + return m.OldSubtotal(ctx) + case cartlineitems.FieldTotal: + return m.OldTotal(ctx) + } + return nil, fmt.Errorf("unknown CartLineItems field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *DiscountMutation) SetField(name string, value ent.Value) error { +func (m *CartLineItemsMutation) SetField(name string, value ent.Value) error { switch name { - case discount.FieldStatus: + case cartlineitems.FieldStatus: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetStatus(v) return nil - case discount.FieldCreatedAt: + case cartlineitems.FieldCreatedAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedAt(v) return nil - case discount.FieldUpdatedAt: + case cartlineitems.FieldUpdatedAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedAt(v) return nil - case discount.FieldCreatedBy: + case cartlineitems.FieldCreatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedBy(v) return nil - case discount.FieldUpdatedBy: + case cartlineitems.FieldUpdatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedBy(v) return nil - case discount.FieldCode: - v, ok := value.(string) + case cartlineitems.FieldMetadata: + v, ok := value.(map[string]string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetCode(v) + m.SetMetadata(v) return nil - case discount.FieldDescription: + case cartlineitems.FieldCartID: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDescription(v) + m.SetCartID(v) return nil - case discount.FieldDiscountType: - v, ok := value.(types.DiscountType) + case cartlineitems.FieldEntityID: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDiscountType(v) + m.SetEntityID(v) return nil - case discount.FieldDiscountValue: - v, ok := value.(decimal.Decimal) + case cartlineitems.FieldEntityType: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDiscountValue(v) + m.SetEntityType(v) return nil - case discount.FieldValidFrom: - v, ok := value.(time.Time) + case cartlineitems.FieldQuantity: + v, ok := value.(int) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetValidFrom(v) + m.SetQuantity(v) return nil - case discount.FieldValidUntil: - v, ok := value.(time.Time) + case cartlineitems.FieldPerUnitPrice: + v, ok := value.(decimal.Decimal) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetValidUntil(v) + m.SetPerUnitPrice(v) return nil - case discount.FieldIsActive: - v, ok := value.(bool) + case cartlineitems.FieldTaxAmount: + v, ok := value.(decimal.Decimal) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetIsActive(v) + m.SetTaxAmount(v) return nil - case discount.FieldMaxUses: - v, ok := value.(int) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetMaxUses(v) - return nil - case discount.FieldMinOrderValue: + case cartlineitems.FieldDiscountAmount: v, ok := value.(decimal.Decimal) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetMinOrderValue(v) + m.SetDiscountAmount(v) return nil - case discount.FieldIsCombinable: - v, ok := value.(bool) + case cartlineitems.FieldSubtotal: + v, ok := value.(decimal.Decimal) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetIsCombinable(v) + m.SetSubtotal(v) return nil - case discount.FieldMetadata: - v, ok := value.(map[string]string) + case cartlineitems.FieldTotal: + v, ok := value.(decimal.Decimal) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetMetadata(v) + m.SetTotal(v) return nil } - return fmt.Errorf("unknown Discount field %s", name) + return fmt.Errorf("unknown CartLineItems field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *DiscountMutation) AddedFields() []string { +func (m *CartLineItemsMutation) AddedFields() []string { var fields []string - if m.addmax_uses != nil { - fields = append(fields, discount.FieldMaxUses) + if m.addquantity != nil { + fields = append(fields, cartlineitems.FieldQuantity) } return fields } @@ -2027,10 +2276,10 @@ func (m *DiscountMutation) AddedFields() []string { // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *DiscountMutation) AddedField(name string) (ent.Value, bool) { +func (m *CartLineItemsMutation) AddedField(name string) (ent.Value, bool) { switch name { - case discount.FieldMaxUses: - return m.AddedMaxUses() + case cartlineitems.FieldQuantity: + return m.AddedQuantity() } return nil, false } @@ -2038,226 +2287,221 @@ func (m *DiscountMutation) AddedField(name string) (ent.Value, bool) { // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *DiscountMutation) AddField(name string, value ent.Value) error { +func (m *CartLineItemsMutation) AddField(name string, value ent.Value) error { switch name { - case discount.FieldMaxUses: + case cartlineitems.FieldQuantity: v, ok := value.(int) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.AddMaxUses(v) + m.AddQuantity(v) return nil } - return fmt.Errorf("unknown Discount numeric field %s", name) + return fmt.Errorf("unknown CartLineItems numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *DiscountMutation) ClearedFields() []string { +func (m *CartLineItemsMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(discount.FieldCreatedBy) { - fields = append(fields, discount.FieldCreatedBy) - } - if m.FieldCleared(discount.FieldUpdatedBy) { - fields = append(fields, discount.FieldUpdatedBy) - } - if m.FieldCleared(discount.FieldDescription) { - fields = append(fields, discount.FieldDescription) - } - if m.FieldCleared(discount.FieldValidUntil) { - fields = append(fields, discount.FieldValidUntil) - } - if m.FieldCleared(discount.FieldMaxUses) { - fields = append(fields, discount.FieldMaxUses) + if m.FieldCleared(cartlineitems.FieldCreatedBy) { + fields = append(fields, cartlineitems.FieldCreatedBy) } - if m.FieldCleared(discount.FieldMinOrderValue) { - fields = append(fields, discount.FieldMinOrderValue) + if m.FieldCleared(cartlineitems.FieldUpdatedBy) { + fields = append(fields, cartlineitems.FieldUpdatedBy) } - if m.FieldCleared(discount.FieldMetadata) { - fields = append(fields, discount.FieldMetadata) + if m.FieldCleared(cartlineitems.FieldMetadata) { + fields = append(fields, cartlineitems.FieldMetadata) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *DiscountMutation) FieldCleared(name string) bool { +func (m *CartLineItemsMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *DiscountMutation) ClearField(name string) error { +func (m *CartLineItemsMutation) ClearField(name string) error { switch name { - case discount.FieldCreatedBy: + case cartlineitems.FieldCreatedBy: m.ClearCreatedBy() return nil - case discount.FieldUpdatedBy: + case cartlineitems.FieldUpdatedBy: m.ClearUpdatedBy() return nil - case discount.FieldDescription: - m.ClearDescription() - return nil - case discount.FieldValidUntil: - m.ClearValidUntil() - return nil - case discount.FieldMaxUses: - m.ClearMaxUses() - return nil - case discount.FieldMinOrderValue: - m.ClearMinOrderValue() - return nil - case discount.FieldMetadata: + case cartlineitems.FieldMetadata: m.ClearMetadata() return nil } - return fmt.Errorf("unknown Discount nullable field %s", name) + return fmt.Errorf("unknown CartLineItems nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *DiscountMutation) ResetField(name string) error { +func (m *CartLineItemsMutation) ResetField(name string) error { switch name { - case discount.FieldStatus: + case cartlineitems.FieldStatus: m.ResetStatus() return nil - case discount.FieldCreatedAt: + case cartlineitems.FieldCreatedAt: m.ResetCreatedAt() return nil - case discount.FieldUpdatedAt: + case cartlineitems.FieldUpdatedAt: m.ResetUpdatedAt() return nil - case discount.FieldCreatedBy: + case cartlineitems.FieldCreatedBy: m.ResetCreatedBy() return nil - case discount.FieldUpdatedBy: + case cartlineitems.FieldUpdatedBy: m.ResetUpdatedBy() return nil - case discount.FieldCode: - m.ResetCode() - return nil - case discount.FieldDescription: - m.ResetDescription() + case cartlineitems.FieldMetadata: + m.ResetMetadata() return nil - case discount.FieldDiscountType: - m.ResetDiscountType() + case cartlineitems.FieldCartID: + m.ResetCartID() return nil - case discount.FieldDiscountValue: - m.ResetDiscountValue() + case cartlineitems.FieldEntityID: + m.ResetEntityID() return nil - case discount.FieldValidFrom: - m.ResetValidFrom() + case cartlineitems.FieldEntityType: + m.ResetEntityType() return nil - case discount.FieldValidUntil: - m.ResetValidUntil() + case cartlineitems.FieldQuantity: + m.ResetQuantity() return nil - case discount.FieldIsActive: - m.ResetIsActive() + case cartlineitems.FieldPerUnitPrice: + m.ResetPerUnitPrice() return nil - case discount.FieldMaxUses: - m.ResetMaxUses() + case cartlineitems.FieldTaxAmount: + m.ResetTaxAmount() return nil - case discount.FieldMinOrderValue: - m.ResetMinOrderValue() + case cartlineitems.FieldDiscountAmount: + m.ResetDiscountAmount() return nil - case discount.FieldIsCombinable: - m.ResetIsCombinable() + case cartlineitems.FieldSubtotal: + m.ResetSubtotal() return nil - case discount.FieldMetadata: - m.ResetMetadata() + case cartlineitems.FieldTotal: + m.ResetTotal() return nil } - return fmt.Errorf("unknown Discount field %s", name) + return fmt.Errorf("unknown CartLineItems field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *DiscountMutation) AddedEdges() []string { - edges := make([]string, 0, 0) +func (m *CartLineItemsMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.cart != nil { + edges = append(edges, cartlineitems.EdgeCart) + } return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *DiscountMutation) AddedIDs(name string) []ent.Value { +func (m *CartLineItemsMutation) AddedIDs(name string) []ent.Value { + switch name { + case cartlineitems.EdgeCart: + if id := m.cart; id != nil { + return []ent.Value{*id} + } + } return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *DiscountMutation) RemovedEdges() []string { - edges := make([]string, 0, 0) +func (m *CartLineItemsMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *DiscountMutation) RemovedIDs(name string) []ent.Value { +func (m *CartLineItemsMutation) RemovedIDs(name string) []ent.Value { return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *DiscountMutation) ClearedEdges() []string { - edges := make([]string, 0, 0) +func (m *CartLineItemsMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedcart { + edges = append(edges, cartlineitems.EdgeCart) + } return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *DiscountMutation) EdgeCleared(name string) bool { +func (m *CartLineItemsMutation) EdgeCleared(name string) bool { + switch name { + case cartlineitems.EdgeCart: + return m.clearedcart + } return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *DiscountMutation) ClearEdge(name string) error { - return fmt.Errorf("unknown Discount unique edge %s", name) +func (m *CartLineItemsMutation) ClearEdge(name string) error { + switch name { + case cartlineitems.EdgeCart: + m.ClearCart() + return nil + } + return fmt.Errorf("unknown CartLineItems unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *DiscountMutation) ResetEdge(name string) error { - return fmt.Errorf("unknown Discount edge %s", name) +func (m *CartLineItemsMutation) ResetEdge(name string) error { + switch name { + case cartlineitems.EdgeCart: + m.ResetCart() + return nil + } + return fmt.Errorf("unknown CartLineItems edge %s", name) } -// FileUploadMutation represents an operation that mutates the FileUpload nodes in the graph. -type FileUploadMutation struct { +// CategoryMutation represents an operation that mutates the Category nodes in the graph. +type CategoryMutation struct { config - op Op - typ string - id *string - status *string - created_at *time.Time - updated_at *time.Time - created_by *string - updated_by *string - file_name *string - file_type *string - extension *string - mime_type *string - public_url *string - secure_url *string - provider *string - external_id *string - size_bytes *int64 - addsize_bytes *int64 - file_size *string - clearedFields map[string]struct{} - done bool - oldValue func(context.Context) (*FileUpload, error) - predicates []predicate.FileUpload + op Op + typ string + id *string + status *string + created_at *time.Time + updated_at *time.Time + created_by *string + updated_by *string + metadata *map[string]string + name *string + lookup_key *string + description *string + clearedFields map[string]struct{} + internships map[string]struct{} + removedinternships map[string]struct{} + clearedinternships bool + done bool + oldValue func(context.Context) (*Category, error) + predicates []predicate.Category } -var _ ent.Mutation = (*FileUploadMutation)(nil) +var _ ent.Mutation = (*CategoryMutation)(nil) -// fileuploadOption allows management of the mutation configuration using functional options. -type fileuploadOption func(*FileUploadMutation) +// categoryOption allows management of the mutation configuration using functional options. +type categoryOption func(*CategoryMutation) -// newFileUploadMutation creates new mutation for the FileUpload entity. -func newFileUploadMutation(c config, op Op, opts ...fileuploadOption) *FileUploadMutation { - m := &FileUploadMutation{ +// newCategoryMutation creates new mutation for the Category entity. +func newCategoryMutation(c config, op Op, opts ...categoryOption) *CategoryMutation { + m := &CategoryMutation{ config: c, op: op, - typ: TypeFileUpload, + typ: TypeCategory, clearedFields: make(map[string]struct{}), } for _, opt := range opts { @@ -2266,20 +2510,20 @@ func newFileUploadMutation(c config, op Op, opts ...fileuploadOption) *FileUploa return m } -// withFileUploadID sets the ID field of the mutation. -func withFileUploadID(id string) fileuploadOption { - return func(m *FileUploadMutation) { +// withCategoryID sets the ID field of the mutation. +func withCategoryID(id string) categoryOption { + return func(m *CategoryMutation) { var ( err error once sync.Once - value *FileUpload + value *Category ) - m.oldValue = func(ctx context.Context) (*FileUpload, error) { + m.oldValue = func(ctx context.Context) (*Category, error) { once.Do(func() { if m.done { err = errors.New("querying old values post mutation is not allowed") } else { - value, err = m.Client().FileUpload.Get(ctx, id) + value, err = m.Client().Category.Get(ctx, id) } }) return value, err @@ -2288,10 +2532,10 @@ func withFileUploadID(id string) fileuploadOption { } } -// withFileUpload sets the old FileUpload of the mutation. -func withFileUpload(node *FileUpload) fileuploadOption { - return func(m *FileUploadMutation) { - m.oldValue = func(context.Context) (*FileUpload, error) { +// withCategory sets the old Category of the mutation. +func withCategory(node *Category) categoryOption { + return func(m *CategoryMutation) { + m.oldValue = func(context.Context) (*Category, error) { return node, nil } m.id = &node.ID @@ -2300,7 +2544,7 @@ func withFileUpload(node *FileUpload) fileuploadOption { // Client returns a new `ent.Client` from the mutation. If the mutation was // executed in a transaction (ent.Tx), a transactional client is returned. -func (m FileUploadMutation) Client() *Client { +func (m CategoryMutation) Client() *Client { client := &Client{config: m.config} client.init() return client @@ -2308,7 +2552,7 @@ func (m FileUploadMutation) Client() *Client { // Tx returns an `ent.Tx` for mutations that were executed in transactions; // it returns an error otherwise. -func (m FileUploadMutation) Tx() (*Tx, error) { +func (m CategoryMutation) Tx() (*Tx, error) { if _, ok := m.driver.(*txDriver); !ok { return nil, errors.New("ent: mutation is not running in a transaction") } @@ -2318,14 +2562,14 @@ func (m FileUploadMutation) Tx() (*Tx, error) { } // SetID sets the value of the id field. Note that this -// operation is only accepted on creation of FileUpload entities. -func (m *FileUploadMutation) SetID(id string) { +// operation is only accepted on creation of Category entities. +func (m *CategoryMutation) SetID(id string) { m.id = &id } // ID returns the ID value in the mutation. Note that the ID is only available // if it was provided to the builder or after it was returned from the database. -func (m *FileUploadMutation) ID() (id string, exists bool) { +func (m *CategoryMutation) ID() (id string, exists bool) { if m.id == nil { return } @@ -2336,7 +2580,7 @@ func (m *FileUploadMutation) ID() (id string, exists bool) { // That means, if the mutation is applied within a transaction with an isolation level such // as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated // or updated by the mutation. -func (m *FileUploadMutation) IDs(ctx context.Context) ([]string, error) { +func (m *CategoryMutation) IDs(ctx context.Context) ([]string, error) { switch { case m.op.Is(OpUpdateOne | OpDeleteOne): id, exists := m.ID() @@ -2345,19 +2589,19 @@ func (m *FileUploadMutation) IDs(ctx context.Context) ([]string, error) { } fallthrough case m.op.Is(OpUpdate | OpDelete): - return m.Client().FileUpload.Query().Where(m.predicates...).IDs(ctx) + return m.Client().Category.Query().Where(m.predicates...).IDs(ctx) default: return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) } } // SetStatus sets the "status" field. -func (m *FileUploadMutation) SetStatus(s string) { +func (m *CategoryMutation) SetStatus(s string) { m.status = &s } // Status returns the value of the "status" field in the mutation. -func (m *FileUploadMutation) Status() (r string, exists bool) { +func (m *CategoryMutation) Status() (r string, exists bool) { v := m.status if v == nil { return @@ -2365,10 +2609,10 @@ func (m *FileUploadMutation) Status() (r string, exists bool) { return *v, true } -// OldStatus returns the old "status" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldStatus returns the old "status" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldStatus(ctx context.Context) (v string, err error) { +func (m *CategoryMutation) OldStatus(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldStatus is only allowed on UpdateOne operations") } @@ -2383,17 +2627,17 @@ func (m *FileUploadMutation) OldStatus(ctx context.Context) (v string, err error } // ResetStatus resets all changes to the "status" field. -func (m *FileUploadMutation) ResetStatus() { +func (m *CategoryMutation) ResetStatus() { m.status = nil } // SetCreatedAt sets the "created_at" field. -func (m *FileUploadMutation) SetCreatedAt(t time.Time) { +func (m *CategoryMutation) SetCreatedAt(t time.Time) { m.created_at = &t } // CreatedAt returns the value of the "created_at" field in the mutation. -func (m *FileUploadMutation) CreatedAt() (r time.Time, exists bool) { +func (m *CategoryMutation) CreatedAt() (r time.Time, exists bool) { v := m.created_at if v == nil { return @@ -2401,10 +2645,10 @@ func (m *FileUploadMutation) CreatedAt() (r time.Time, exists bool) { return *v, true } -// OldCreatedAt returns the old "created_at" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedAt returns the old "created_at" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { +func (m *CategoryMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") } @@ -2419,17 +2663,17 @@ func (m *FileUploadMutation) OldCreatedAt(ctx context.Context) (v time.Time, err } // ResetCreatedAt resets all changes to the "created_at" field. -func (m *FileUploadMutation) ResetCreatedAt() { +func (m *CategoryMutation) ResetCreatedAt() { m.created_at = nil } // SetUpdatedAt sets the "updated_at" field. -func (m *FileUploadMutation) SetUpdatedAt(t time.Time) { +func (m *CategoryMutation) SetUpdatedAt(t time.Time) { m.updated_at = &t } // UpdatedAt returns the value of the "updated_at" field in the mutation. -func (m *FileUploadMutation) UpdatedAt() (r time.Time, exists bool) { +func (m *CategoryMutation) UpdatedAt() (r time.Time, exists bool) { v := m.updated_at if v == nil { return @@ -2437,10 +2681,10 @@ func (m *FileUploadMutation) UpdatedAt() (r time.Time, exists bool) { return *v, true } -// OldUpdatedAt returns the old "updated_at" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedAt returns the old "updated_at" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { +func (m *CategoryMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") } @@ -2455,17 +2699,17 @@ func (m *FileUploadMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err } // ResetUpdatedAt resets all changes to the "updated_at" field. -func (m *FileUploadMutation) ResetUpdatedAt() { +func (m *CategoryMutation) ResetUpdatedAt() { m.updated_at = nil } // SetCreatedBy sets the "created_by" field. -func (m *FileUploadMutation) SetCreatedBy(s string) { +func (m *CategoryMutation) SetCreatedBy(s string) { m.created_by = &s } // CreatedBy returns the value of the "created_by" field in the mutation. -func (m *FileUploadMutation) CreatedBy() (r string, exists bool) { +func (m *CategoryMutation) CreatedBy() (r string, exists bool) { v := m.created_by if v == nil { return @@ -2473,10 +2717,10 @@ func (m *FileUploadMutation) CreatedBy() (r string, exists bool) { return *v, true } -// OldCreatedBy returns the old "created_by" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedBy returns the old "created_by" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldCreatedBy(ctx context.Context) (v string, err error) { +func (m *CategoryMutation) OldCreatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") } @@ -2491,30 +2735,30 @@ func (m *FileUploadMutation) OldCreatedBy(ctx context.Context) (v string, err er } // ClearCreatedBy clears the value of the "created_by" field. -func (m *FileUploadMutation) ClearCreatedBy() { +func (m *CategoryMutation) ClearCreatedBy() { m.created_by = nil - m.clearedFields[fileupload.FieldCreatedBy] = struct{}{} + m.clearedFields[category.FieldCreatedBy] = struct{}{} } // CreatedByCleared returns if the "created_by" field was cleared in this mutation. -func (m *FileUploadMutation) CreatedByCleared() bool { - _, ok := m.clearedFields[fileupload.FieldCreatedBy] +func (m *CategoryMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[category.FieldCreatedBy] return ok } // ResetCreatedBy resets all changes to the "created_by" field. -func (m *FileUploadMutation) ResetCreatedBy() { +func (m *CategoryMutation) ResetCreatedBy() { m.created_by = nil - delete(m.clearedFields, fileupload.FieldCreatedBy) + delete(m.clearedFields, category.FieldCreatedBy) } // SetUpdatedBy sets the "updated_by" field. -func (m *FileUploadMutation) SetUpdatedBy(s string) { +func (m *CategoryMutation) SetUpdatedBy(s string) { m.updated_by = &s } // UpdatedBy returns the value of the "updated_by" field in the mutation. -func (m *FileUploadMutation) UpdatedBy() (r string, exists bool) { +func (m *CategoryMutation) UpdatedBy() (r string, exists bool) { v := m.updated_by if v == nil { return @@ -2522,10 +2766,10 @@ func (m *FileUploadMutation) UpdatedBy() (r string, exists bool) { return *v, true } -// OldUpdatedBy returns the old "updated_by" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedBy returns the old "updated_by" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { +func (m *CategoryMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") } @@ -2540,438 +2784,256 @@ func (m *FileUploadMutation) OldUpdatedBy(ctx context.Context) (v string, err er } // ClearUpdatedBy clears the value of the "updated_by" field. -func (m *FileUploadMutation) ClearUpdatedBy() { +func (m *CategoryMutation) ClearUpdatedBy() { m.updated_by = nil - m.clearedFields[fileupload.FieldUpdatedBy] = struct{}{} + m.clearedFields[category.FieldUpdatedBy] = struct{}{} } // UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. -func (m *FileUploadMutation) UpdatedByCleared() bool { - _, ok := m.clearedFields[fileupload.FieldUpdatedBy] +func (m *CategoryMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[category.FieldUpdatedBy] return ok } // ResetUpdatedBy resets all changes to the "updated_by" field. -func (m *FileUploadMutation) ResetUpdatedBy() { +func (m *CategoryMutation) ResetUpdatedBy() { m.updated_by = nil - delete(m.clearedFields, fileupload.FieldUpdatedBy) + delete(m.clearedFields, category.FieldUpdatedBy) } -// SetFileName sets the "file_name" field. -func (m *FileUploadMutation) SetFileName(s string) { - m.file_name = &s +// SetMetadata sets the "metadata" field. +func (m *CategoryMutation) SetMetadata(value map[string]string) { + m.metadata = &value } -// FileName returns the value of the "file_name" field in the mutation. -func (m *FileUploadMutation) FileName() (r string, exists bool) { - v := m.file_name +// Metadata returns the value of the "metadata" field in the mutation. +func (m *CategoryMutation) Metadata() (r map[string]string, exists bool) { + v := m.metadata if v == nil { return } return *v, true } -// OldFileName returns the old "file_name" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldMetadata returns the old "metadata" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldFileName(ctx context.Context) (v string, err error) { +func (m *CategoryMutation) OldMetadata(ctx context.Context) (v map[string]string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldFileName is only allowed on UpdateOne operations") + return v, errors.New("OldMetadata is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldFileName requires an ID field in the mutation") + return v, errors.New("OldMetadata requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldFileName: %w", err) + return v, fmt.Errorf("querying old value for OldMetadata: %w", err) } - return oldValue.FileName, nil -} - -// ResetFileName resets all changes to the "file_name" field. -func (m *FileUploadMutation) ResetFileName() { - m.file_name = nil -} - -// SetFileType sets the "file_type" field. -func (m *FileUploadMutation) SetFileType(s string) { - m.file_type = &s + return oldValue.Metadata, nil } -// FileType returns the value of the "file_type" field in the mutation. -func (m *FileUploadMutation) FileType() (r string, exists bool) { - v := m.file_type - if v == nil { - return - } - return *v, true +// ClearMetadata clears the value of the "metadata" field. +func (m *CategoryMutation) ClearMetadata() { + m.metadata = nil + m.clearedFields[category.FieldMetadata] = struct{}{} } -// OldFileType returns the old "file_type" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldFileType(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldFileType is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldFileType requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldFileType: %w", err) - } - return oldValue.FileType, nil +// MetadataCleared returns if the "metadata" field was cleared in this mutation. +func (m *CategoryMutation) MetadataCleared() bool { + _, ok := m.clearedFields[category.FieldMetadata] + return ok } -// ResetFileType resets all changes to the "file_type" field. -func (m *FileUploadMutation) ResetFileType() { - m.file_type = nil +// ResetMetadata resets all changes to the "metadata" field. +func (m *CategoryMutation) ResetMetadata() { + m.metadata = nil + delete(m.clearedFields, category.FieldMetadata) } -// SetExtension sets the "extension" field. -func (m *FileUploadMutation) SetExtension(s string) { - m.extension = &s +// SetName sets the "name" field. +func (m *CategoryMutation) SetName(s string) { + m.name = &s } -// Extension returns the value of the "extension" field in the mutation. -func (m *FileUploadMutation) Extension() (r string, exists bool) { - v := m.extension +// Name returns the value of the "name" field in the mutation. +func (m *CategoryMutation) Name() (r string, exists bool) { + v := m.name if v == nil { return } return *v, true } -// OldExtension returns the old "extension" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldName returns the old "name" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldExtension(ctx context.Context) (v string, err error) { +func (m *CategoryMutation) OldName(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldExtension is only allowed on UpdateOne operations") + return v, errors.New("OldName is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldExtension requires an ID field in the mutation") + return v, errors.New("OldName requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldExtension: %w", err) + return v, fmt.Errorf("querying old value for OldName: %w", err) } - return oldValue.Extension, nil + return oldValue.Name, nil } -// ResetExtension resets all changes to the "extension" field. -func (m *FileUploadMutation) ResetExtension() { - m.extension = nil +// ResetName resets all changes to the "name" field. +func (m *CategoryMutation) ResetName() { + m.name = nil } -// SetMimeType sets the "mime_type" field. -func (m *FileUploadMutation) SetMimeType(s string) { - m.mime_type = &s +// SetLookupKey sets the "lookup_key" field. +func (m *CategoryMutation) SetLookupKey(s string) { + m.lookup_key = &s } -// MimeType returns the value of the "mime_type" field in the mutation. -func (m *FileUploadMutation) MimeType() (r string, exists bool) { - v := m.mime_type +// LookupKey returns the value of the "lookup_key" field in the mutation. +func (m *CategoryMutation) LookupKey() (r string, exists bool) { + v := m.lookup_key if v == nil { return } return *v, true } -// OldMimeType returns the old "mime_type" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldLookupKey returns the old "lookup_key" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldMimeType(ctx context.Context) (v string, err error) { +func (m *CategoryMutation) OldLookupKey(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldMimeType is only allowed on UpdateOne operations") + return v, errors.New("OldLookupKey is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldMimeType requires an ID field in the mutation") + return v, errors.New("OldLookupKey requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldMimeType: %w", err) + return v, fmt.Errorf("querying old value for OldLookupKey: %w", err) } - return oldValue.MimeType, nil + return oldValue.LookupKey, nil } -// ResetMimeType resets all changes to the "mime_type" field. -func (m *FileUploadMutation) ResetMimeType() { - m.mime_type = nil +// ResetLookupKey resets all changes to the "lookup_key" field. +func (m *CategoryMutation) ResetLookupKey() { + m.lookup_key = nil } -// SetPublicURL sets the "public_url" field. -func (m *FileUploadMutation) SetPublicURL(s string) { - m.public_url = &s +// SetDescription sets the "description" field. +func (m *CategoryMutation) SetDescription(s string) { + m.description = &s } -// PublicURL returns the value of the "public_url" field in the mutation. -func (m *FileUploadMutation) PublicURL() (r string, exists bool) { - v := m.public_url +// Description returns the value of the "description" field in the mutation. +func (m *CategoryMutation) Description() (r string, exists bool) { + v := m.description if v == nil { return } return *v, true } -// OldPublicURL returns the old "public_url" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// OldDescription returns the old "description" field's value of the Category entity. +// If the Category object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldPublicURL(ctx context.Context) (v string, err error) { +func (m *CategoryMutation) OldDescription(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPublicURL is only allowed on UpdateOne operations") + return v, errors.New("OldDescription is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPublicURL requires an ID field in the mutation") + return v, errors.New("OldDescription requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldPublicURL: %w", err) + return v, fmt.Errorf("querying old value for OldDescription: %w", err) } - return oldValue.PublicURL, nil + return oldValue.Description, nil } -// ResetPublicURL resets all changes to the "public_url" field. -func (m *FileUploadMutation) ResetPublicURL() { - m.public_url = nil +// ClearDescription clears the value of the "description" field. +func (m *CategoryMutation) ClearDescription() { + m.description = nil + m.clearedFields[category.FieldDescription] = struct{}{} } -// SetSecureURL sets the "secure_url" field. -func (m *FileUploadMutation) SetSecureURL(s string) { - m.secure_url = &s +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *CategoryMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[category.FieldDescription] + return ok } -// SecureURL returns the value of the "secure_url" field in the mutation. -func (m *FileUploadMutation) SecureURL() (r string, exists bool) { - v := m.secure_url - if v == nil { - return - } - return *v, true +// ResetDescription resets all changes to the "description" field. +func (m *CategoryMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, category.FieldDescription) } -// OldSecureURL returns the old "secure_url" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldSecureURL(ctx context.Context) (v *string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldSecureURL is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldSecureURL requires an ID field in the mutation") +// AddInternshipIDs adds the "internships" edge to the Internship entity by ids. +func (m *CategoryMutation) AddInternshipIDs(ids ...string) { + if m.internships == nil { + m.internships = make(map[string]struct{}) } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldSecureURL: %w", err) + for i := range ids { + m.internships[ids[i]] = struct{}{} } - return oldValue.SecureURL, nil } -// ClearSecureURL clears the value of the "secure_url" field. -func (m *FileUploadMutation) ClearSecureURL() { - m.secure_url = nil - m.clearedFields[fileupload.FieldSecureURL] = struct{}{} +// ClearInternships clears the "internships" edge to the Internship entity. +func (m *CategoryMutation) ClearInternships() { + m.clearedinternships = true } -// SecureURLCleared returns if the "secure_url" field was cleared in this mutation. -func (m *FileUploadMutation) SecureURLCleared() bool { - _, ok := m.clearedFields[fileupload.FieldSecureURL] - return ok +// InternshipsCleared reports if the "internships" edge to the Internship entity was cleared. +func (m *CategoryMutation) InternshipsCleared() bool { + return m.clearedinternships } -// ResetSecureURL resets all changes to the "secure_url" field. -func (m *FileUploadMutation) ResetSecureURL() { - m.secure_url = nil - delete(m.clearedFields, fileupload.FieldSecureURL) -} - -// SetProvider sets the "provider" field. -func (m *FileUploadMutation) SetProvider(s string) { - m.provider = &s -} - -// Provider returns the value of the "provider" field in the mutation. -func (m *FileUploadMutation) Provider() (r string, exists bool) { - v := m.provider - if v == nil { - return - } - return *v, true -} - -// OldProvider returns the old "provider" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldProvider(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldProvider is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldProvider requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldProvider: %w", err) - } - return oldValue.Provider, nil -} - -// ResetProvider resets all changes to the "provider" field. -func (m *FileUploadMutation) ResetProvider() { - m.provider = nil -} - -// SetExternalID sets the "external_id" field. -func (m *FileUploadMutation) SetExternalID(s string) { - m.external_id = &s -} - -// ExternalID returns the value of the "external_id" field in the mutation. -func (m *FileUploadMutation) ExternalID() (r string, exists bool) { - v := m.external_id - if v == nil { - return - } - return *v, true -} - -// OldExternalID returns the old "external_id" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldExternalID(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldExternalID is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldExternalID requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldExternalID: %w", err) - } - return oldValue.ExternalID, nil -} - -// ResetExternalID resets all changes to the "external_id" field. -func (m *FileUploadMutation) ResetExternalID() { - m.external_id = nil -} - -// SetSizeBytes sets the "size_bytes" field. -func (m *FileUploadMutation) SetSizeBytes(i int64) { - m.size_bytes = &i - m.addsize_bytes = nil -} - -// SizeBytes returns the value of the "size_bytes" field in the mutation. -func (m *FileUploadMutation) SizeBytes() (r int64, exists bool) { - v := m.size_bytes - if v == nil { - return - } - return *v, true -} - -// OldSizeBytes returns the old "size_bytes" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldSizeBytes(ctx context.Context) (v int64, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldSizeBytes is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldSizeBytes requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldSizeBytes: %w", err) - } - return oldValue.SizeBytes, nil -} - -// AddSizeBytes adds i to the "size_bytes" field. -func (m *FileUploadMutation) AddSizeBytes(i int64) { - if m.addsize_bytes != nil { - *m.addsize_bytes += i - } else { - m.addsize_bytes = &i +// RemoveInternshipIDs removes the "internships" edge to the Internship entity by IDs. +func (m *CategoryMutation) RemoveInternshipIDs(ids ...string) { + if m.removedinternships == nil { + m.removedinternships = make(map[string]struct{}) } -} - -// AddedSizeBytes returns the value that was added to the "size_bytes" field in this mutation. -func (m *FileUploadMutation) AddedSizeBytes() (r int64, exists bool) { - v := m.addsize_bytes - if v == nil { - return + for i := range ids { + delete(m.internships, ids[i]) + m.removedinternships[ids[i]] = struct{}{} } - return *v, true -} - -// ResetSizeBytes resets all changes to the "size_bytes" field. -func (m *FileUploadMutation) ResetSizeBytes() { - m.size_bytes = nil - m.addsize_bytes = nil -} - -// SetFileSize sets the "file_size" field. -func (m *FileUploadMutation) SetFileSize(s string) { - m.file_size = &s } -// FileSize returns the value of the "file_size" field in the mutation. -func (m *FileUploadMutation) FileSize() (r string, exists bool) { - v := m.file_size - if v == nil { - return +// RemovedInternships returns the removed IDs of the "internships" edge to the Internship entity. +func (m *CategoryMutation) RemovedInternshipsIDs() (ids []string) { + for id := range m.removedinternships { + ids = append(ids, id) } - return *v, true + return } -// OldFileSize returns the old "file_size" field's value of the FileUpload entity. -// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *FileUploadMutation) OldFileSize(ctx context.Context) (v *string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldFileSize is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldFileSize requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldFileSize: %w", err) +// InternshipsIDs returns the "internships" edge IDs in the mutation. +func (m *CategoryMutation) InternshipsIDs() (ids []string) { + for id := range m.internships { + ids = append(ids, id) } - return oldValue.FileSize, nil -} - -// ClearFileSize clears the value of the "file_size" field. -func (m *FileUploadMutation) ClearFileSize() { - m.file_size = nil - m.clearedFields[fileupload.FieldFileSize] = struct{}{} -} - -// FileSizeCleared returns if the "file_size" field was cleared in this mutation. -func (m *FileUploadMutation) FileSizeCleared() bool { - _, ok := m.clearedFields[fileupload.FieldFileSize] - return ok + return } -// ResetFileSize resets all changes to the "file_size" field. -func (m *FileUploadMutation) ResetFileSize() { - m.file_size = nil - delete(m.clearedFields, fileupload.FieldFileSize) +// ResetInternships resets all changes to the "internships" edge. +func (m *CategoryMutation) ResetInternships() { + m.internships = nil + m.clearedinternships = false + m.removedinternships = nil } -// Where appends a list predicates to the FileUploadMutation builder. -func (m *FileUploadMutation) Where(ps ...predicate.FileUpload) { +// Where appends a list predicates to the CategoryMutation builder. +func (m *CategoryMutation) Where(ps ...predicate.Category) { m.predicates = append(m.predicates, ps...) } -// WhereP appends storage-level predicates to the FileUploadMutation builder. Using this method, +// WhereP appends storage-level predicates to the CategoryMutation builder. Using this method, // users can use type-assertion to append predicates that do not depend on any generated package. -func (m *FileUploadMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.FileUpload, len(ps)) +func (m *CategoryMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Category, len(ps)) for i := range ps { p[i] = ps[i] } @@ -2979,69 +3041,51 @@ func (m *FileUploadMutation) WhereP(ps ...func(*sql.Selector)) { } // Op returns the operation name. -func (m *FileUploadMutation) Op() Op { +func (m *CategoryMutation) Op() Op { return m.op } // SetOp allows setting the mutation operation. -func (m *FileUploadMutation) SetOp(op Op) { +func (m *CategoryMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (FileUpload). -func (m *FileUploadMutation) Type() string { +// Type returns the node type of this mutation (Category). +func (m *CategoryMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *FileUploadMutation) Fields() []string { - fields := make([]string, 0, 15) +func (m *CategoryMutation) Fields() []string { + fields := make([]string, 0, 9) if m.status != nil { - fields = append(fields, fileupload.FieldStatus) + fields = append(fields, category.FieldStatus) } if m.created_at != nil { - fields = append(fields, fileupload.FieldCreatedAt) + fields = append(fields, category.FieldCreatedAt) } if m.updated_at != nil { - fields = append(fields, fileupload.FieldUpdatedAt) + fields = append(fields, category.FieldUpdatedAt) } if m.created_by != nil { - fields = append(fields, fileupload.FieldCreatedBy) + fields = append(fields, category.FieldCreatedBy) } if m.updated_by != nil { - fields = append(fields, fileupload.FieldUpdatedBy) - } - if m.file_name != nil { - fields = append(fields, fileupload.FieldFileName) - } - if m.file_type != nil { - fields = append(fields, fileupload.FieldFileType) - } - if m.extension != nil { - fields = append(fields, fileupload.FieldExtension) - } - if m.mime_type != nil { - fields = append(fields, fileupload.FieldMimeType) - } - if m.public_url != nil { - fields = append(fields, fileupload.FieldPublicURL) - } - if m.secure_url != nil { - fields = append(fields, fileupload.FieldSecureURL) + fields = append(fields, category.FieldUpdatedBy) } - if m.provider != nil { - fields = append(fields, fileupload.FieldProvider) + if m.metadata != nil { + fields = append(fields, category.FieldMetadata) } - if m.external_id != nil { - fields = append(fields, fileupload.FieldExternalID) + if m.name != nil { + fields = append(fields, category.FieldName) } - if m.size_bytes != nil { - fields = append(fields, fileupload.FieldSizeBytes) + if m.lookup_key != nil { + fields = append(fields, category.FieldLookupKey) } - if m.file_size != nil { - fields = append(fields, fileupload.FieldFileSize) + if m.description != nil { + fields = append(fields, category.FieldDescription) } return fields } @@ -3049,38 +3093,26 @@ func (m *FileUploadMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *FileUploadMutation) Field(name string) (ent.Value, bool) { +func (m *CategoryMutation) Field(name string) (ent.Value, bool) { switch name { - case fileupload.FieldStatus: + case category.FieldStatus: return m.Status() - case fileupload.FieldCreatedAt: + case category.FieldCreatedAt: return m.CreatedAt() - case fileupload.FieldUpdatedAt: + case category.FieldUpdatedAt: return m.UpdatedAt() - case fileupload.FieldCreatedBy: + case category.FieldCreatedBy: return m.CreatedBy() - case fileupload.FieldUpdatedBy: + case category.FieldUpdatedBy: return m.UpdatedBy() - case fileupload.FieldFileName: - return m.FileName() - case fileupload.FieldFileType: - return m.FileType() - case fileupload.FieldExtension: - return m.Extension() - case fileupload.FieldMimeType: - return m.MimeType() - case fileupload.FieldPublicURL: - return m.PublicURL() - case fileupload.FieldSecureURL: - return m.SecureURL() - case fileupload.FieldProvider: - return m.Provider() - case fileupload.FieldExternalID: - return m.ExternalID() - case fileupload.FieldSizeBytes: - return m.SizeBytes() - case fileupload.FieldFileSize: - return m.FileSize() + case category.FieldMetadata: + return m.Metadata() + case category.FieldName: + return m.Name() + case category.FieldLookupKey: + return m.LookupKey() + case category.FieldDescription: + return m.Description() } return nil, false } @@ -3088,390 +3120,329 @@ func (m *FileUploadMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *FileUploadMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *CategoryMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case fileupload.FieldStatus: + case category.FieldStatus: return m.OldStatus(ctx) - case fileupload.FieldCreatedAt: + case category.FieldCreatedAt: return m.OldCreatedAt(ctx) - case fileupload.FieldUpdatedAt: + case category.FieldUpdatedAt: return m.OldUpdatedAt(ctx) - case fileupload.FieldCreatedBy: + case category.FieldCreatedBy: return m.OldCreatedBy(ctx) - case fileupload.FieldUpdatedBy: + case category.FieldUpdatedBy: return m.OldUpdatedBy(ctx) - case fileupload.FieldFileName: - return m.OldFileName(ctx) - case fileupload.FieldFileType: - return m.OldFileType(ctx) - case fileupload.FieldExtension: - return m.OldExtension(ctx) - case fileupload.FieldMimeType: - return m.OldMimeType(ctx) - case fileupload.FieldPublicURL: - return m.OldPublicURL(ctx) - case fileupload.FieldSecureURL: - return m.OldSecureURL(ctx) - case fileupload.FieldProvider: - return m.OldProvider(ctx) - case fileupload.FieldExternalID: - return m.OldExternalID(ctx) - case fileupload.FieldSizeBytes: - return m.OldSizeBytes(ctx) - case fileupload.FieldFileSize: - return m.OldFileSize(ctx) + case category.FieldMetadata: + return m.OldMetadata(ctx) + case category.FieldName: + return m.OldName(ctx) + case category.FieldLookupKey: + return m.OldLookupKey(ctx) + case category.FieldDescription: + return m.OldDescription(ctx) } - return nil, fmt.Errorf("unknown FileUpload field %s", name) + return nil, fmt.Errorf("unknown Category field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *FileUploadMutation) SetField(name string, value ent.Value) error { +func (m *CategoryMutation) SetField(name string, value ent.Value) error { switch name { - case fileupload.FieldStatus: + case category.FieldStatus: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetStatus(v) return nil - case fileupload.FieldCreatedAt: + case category.FieldCreatedAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedAt(v) return nil - case fileupload.FieldUpdatedAt: + case category.FieldUpdatedAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedAt(v) return nil - case fileupload.FieldCreatedBy: + case category.FieldCreatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedBy(v) return nil - case fileupload.FieldUpdatedBy: + case category.FieldUpdatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedBy(v) return nil - case fileupload.FieldFileName: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetFileName(v) - return nil - case fileupload.FieldFileType: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetFileType(v) - return nil - case fileupload.FieldExtension: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetExtension(v) - return nil - case fileupload.FieldMimeType: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetMimeType(v) - return nil - case fileupload.FieldPublicURL: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetPublicURL(v) - return nil - case fileupload.FieldSecureURL: - v, ok := value.(string) + case category.FieldMetadata: + v, ok := value.(map[string]string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetSecureURL(v) + m.SetMetadata(v) return nil - case fileupload.FieldProvider: + case category.FieldName: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetProvider(v) + m.SetName(v) return nil - case fileupload.FieldExternalID: + case category.FieldLookupKey: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetExternalID(v) - return nil - case fileupload.FieldSizeBytes: - v, ok := value.(int64) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetSizeBytes(v) + m.SetLookupKey(v) return nil - case fileupload.FieldFileSize: + case category.FieldDescription: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetFileSize(v) + m.SetDescription(v) return nil } - return fmt.Errorf("unknown FileUpload field %s", name) + return fmt.Errorf("unknown Category field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *FileUploadMutation) AddedFields() []string { - var fields []string - if m.addsize_bytes != nil { - fields = append(fields, fileupload.FieldSizeBytes) - } - return fields +func (m *CategoryMutation) AddedFields() []string { + return nil } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *FileUploadMutation) AddedField(name string) (ent.Value, bool) { - switch name { - case fileupload.FieldSizeBytes: - return m.AddedSizeBytes() - } +func (m *CategoryMutation) AddedField(name string) (ent.Value, bool) { return nil, false } // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *FileUploadMutation) AddField(name string, value ent.Value) error { +func (m *CategoryMutation) AddField(name string, value ent.Value) error { switch name { - case fileupload.FieldSizeBytes: - v, ok := value.(int64) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.AddSizeBytes(v) - return nil } - return fmt.Errorf("unknown FileUpload numeric field %s", name) + return fmt.Errorf("unknown Category numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *FileUploadMutation) ClearedFields() []string { +func (m *CategoryMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(fileupload.FieldCreatedBy) { - fields = append(fields, fileupload.FieldCreatedBy) + if m.FieldCleared(category.FieldCreatedBy) { + fields = append(fields, category.FieldCreatedBy) } - if m.FieldCleared(fileupload.FieldUpdatedBy) { - fields = append(fields, fileupload.FieldUpdatedBy) + if m.FieldCleared(category.FieldUpdatedBy) { + fields = append(fields, category.FieldUpdatedBy) } - if m.FieldCleared(fileupload.FieldSecureURL) { - fields = append(fields, fileupload.FieldSecureURL) + if m.FieldCleared(category.FieldMetadata) { + fields = append(fields, category.FieldMetadata) } - if m.FieldCleared(fileupload.FieldFileSize) { - fields = append(fields, fileupload.FieldFileSize) + if m.FieldCleared(category.FieldDescription) { + fields = append(fields, category.FieldDescription) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *FileUploadMutation) FieldCleared(name string) bool { +func (m *CategoryMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *FileUploadMutation) ClearField(name string) error { +func (m *CategoryMutation) ClearField(name string) error { switch name { - case fileupload.FieldCreatedBy: + case category.FieldCreatedBy: m.ClearCreatedBy() return nil - case fileupload.FieldUpdatedBy: + case category.FieldUpdatedBy: m.ClearUpdatedBy() return nil - case fileupload.FieldSecureURL: - m.ClearSecureURL() + case category.FieldMetadata: + m.ClearMetadata() return nil - case fileupload.FieldFileSize: - m.ClearFileSize() + case category.FieldDescription: + m.ClearDescription() return nil } - return fmt.Errorf("unknown FileUpload nullable field %s", name) + return fmt.Errorf("unknown Category nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *FileUploadMutation) ResetField(name string) error { +func (m *CategoryMutation) ResetField(name string) error { switch name { - case fileupload.FieldStatus: + case category.FieldStatus: m.ResetStatus() return nil - case fileupload.FieldCreatedAt: + case category.FieldCreatedAt: m.ResetCreatedAt() return nil - case fileupload.FieldUpdatedAt: + case category.FieldUpdatedAt: m.ResetUpdatedAt() return nil - case fileupload.FieldCreatedBy: + case category.FieldCreatedBy: m.ResetCreatedBy() return nil - case fileupload.FieldUpdatedBy: + case category.FieldUpdatedBy: m.ResetUpdatedBy() return nil - case fileupload.FieldFileName: - m.ResetFileName() - return nil - case fileupload.FieldFileType: - m.ResetFileType() - return nil - case fileupload.FieldExtension: - m.ResetExtension() - return nil - case fileupload.FieldMimeType: - m.ResetMimeType() - return nil - case fileupload.FieldPublicURL: - m.ResetPublicURL() - return nil - case fileupload.FieldSecureURL: - m.ResetSecureURL() - return nil - case fileupload.FieldProvider: - m.ResetProvider() + case category.FieldMetadata: + m.ResetMetadata() return nil - case fileupload.FieldExternalID: - m.ResetExternalID() + case category.FieldName: + m.ResetName() return nil - case fileupload.FieldSizeBytes: - m.ResetSizeBytes() + case category.FieldLookupKey: + m.ResetLookupKey() return nil - case fileupload.FieldFileSize: - m.ResetFileSize() + case category.FieldDescription: + m.ResetDescription() return nil } - return fmt.Errorf("unknown FileUpload field %s", name) + return fmt.Errorf("unknown Category field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *FileUploadMutation) AddedEdges() []string { - edges := make([]string, 0, 0) +func (m *CategoryMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.internships != nil { + edges = append(edges, category.EdgeInternships) + } return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *FileUploadMutation) AddedIDs(name string) []ent.Value { +func (m *CategoryMutation) AddedIDs(name string) []ent.Value { + switch name { + case category.EdgeInternships: + ids := make([]ent.Value, 0, len(m.internships)) + for id := range m.internships { + ids = append(ids, id) + } + return ids + } return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *FileUploadMutation) RemovedEdges() []string { - edges := make([]string, 0, 0) +func (m *CategoryMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + if m.removedinternships != nil { + edges = append(edges, category.EdgeInternships) + } return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *FileUploadMutation) RemovedIDs(name string) []ent.Value { +func (m *CategoryMutation) RemovedIDs(name string) []ent.Value { + switch name { + case category.EdgeInternships: + ids := make([]ent.Value, 0, len(m.removedinternships)) + for id := range m.removedinternships { + ids = append(ids, id) + } + return ids + } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *FileUploadMutation) ClearedEdges() []string { - edges := make([]string, 0, 0) +func (m *CategoryMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedinternships { + edges = append(edges, category.EdgeInternships) + } return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *FileUploadMutation) EdgeCleared(name string) bool { +func (m *CategoryMutation) EdgeCleared(name string) bool { + switch name { + case category.EdgeInternships: + return m.clearedinternships + } return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *FileUploadMutation) ClearEdge(name string) error { - return fmt.Errorf("unknown FileUpload unique edge %s", name) +func (m *CategoryMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown Category unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *FileUploadMutation) ResetEdge(name string) error { - return fmt.Errorf("unknown FileUpload edge %s", name) +func (m *CategoryMutation) ResetEdge(name string) error { + switch name { + case category.EdgeInternships: + m.ResetInternships() + return nil + } + return fmt.Errorf("unknown Category edge %s", name) } -// InternshipMutation represents an operation that mutates the Internship nodes in the graph. -type InternshipMutation struct { +// DiscountMutation represents an operation that mutates the Discount nodes in the graph. +type DiscountMutation struct { config - op Op - typ string - id *string - status *string - created_at *time.Time - updated_at *time.Time - created_by *string - updated_by *string - title *string - lookup_key *string - description *string - skills *[]string - appendskills []string - level *string - mode *string - duration_in_weeks *int - addduration_in_weeks *int - learning_outcomes *[]string - appendlearning_outcomes []string - prerequisites *[]string - appendprerequisites []string - benefits *[]string - appendbenefits []string - currency *string - price *decimal.Decimal - flat_discount *decimal.Decimal - percentage_discount *decimal.Decimal - clearedFields map[string]struct{} - categories map[string]struct{} - removedcategories map[string]struct{} - clearedcategories bool - done bool - oldValue func(context.Context) (*Internship, error) - predicates []predicate.Internship + op Op + typ string + id *string + status *string + created_at *time.Time + updated_at *time.Time + created_by *string + updated_by *string + code *string + description *string + discount_type *types.DiscountType + discount_value *decimal.Decimal + valid_from *time.Time + valid_until *time.Time + is_active *bool + max_uses *int + addmax_uses *int + min_order_value *decimal.Decimal + is_combinable *bool + metadata *map[string]string + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Discount, error) + predicates []predicate.Discount } -var _ ent.Mutation = (*InternshipMutation)(nil) +var _ ent.Mutation = (*DiscountMutation)(nil) -// internshipOption allows management of the mutation configuration using functional options. -type internshipOption func(*InternshipMutation) +// discountOption allows management of the mutation configuration using functional options. +type discountOption func(*DiscountMutation) -// newInternshipMutation creates new mutation for the Internship entity. -func newInternshipMutation(c config, op Op, opts ...internshipOption) *InternshipMutation { - m := &InternshipMutation{ +// newDiscountMutation creates new mutation for the Discount entity. +func newDiscountMutation(c config, op Op, opts ...discountOption) *DiscountMutation { + m := &DiscountMutation{ config: c, op: op, - typ: TypeInternship, + typ: TypeDiscount, clearedFields: make(map[string]struct{}), } for _, opt := range opts { @@ -3480,20 +3451,20 @@ func newInternshipMutation(c config, op Op, opts ...internshipOption) *Internshi return m } -// withInternshipID sets the ID field of the mutation. -func withInternshipID(id string) internshipOption { - return func(m *InternshipMutation) { +// withDiscountID sets the ID field of the mutation. +func withDiscountID(id string) discountOption { + return func(m *DiscountMutation) { var ( err error once sync.Once - value *Internship + value *Discount ) - m.oldValue = func(ctx context.Context) (*Internship, error) { + m.oldValue = func(ctx context.Context) (*Discount, error) { once.Do(func() { if m.done { err = errors.New("querying old values post mutation is not allowed") } else { - value, err = m.Client().Internship.Get(ctx, id) + value, err = m.Client().Discount.Get(ctx, id) } }) return value, err @@ -3502,10 +3473,10 @@ func withInternshipID(id string) internshipOption { } } -// withInternship sets the old Internship of the mutation. -func withInternship(node *Internship) internshipOption { - return func(m *InternshipMutation) { - m.oldValue = func(context.Context) (*Internship, error) { +// withDiscount sets the old Discount of the mutation. +func withDiscount(node *Discount) discountOption { + return func(m *DiscountMutation) { + m.oldValue = func(context.Context) (*Discount, error) { return node, nil } m.id = &node.ID @@ -3514,7 +3485,7 @@ func withInternship(node *Internship) internshipOption { // Client returns a new `ent.Client` from the mutation. If the mutation was // executed in a transaction (ent.Tx), a transactional client is returned. -func (m InternshipMutation) Client() *Client { +func (m DiscountMutation) Client() *Client { client := &Client{config: m.config} client.init() return client @@ -3522,7 +3493,7 @@ func (m InternshipMutation) Client() *Client { // Tx returns an `ent.Tx` for mutations that were executed in transactions; // it returns an error otherwise. -func (m InternshipMutation) Tx() (*Tx, error) { +func (m DiscountMutation) Tx() (*Tx, error) { if _, ok := m.driver.(*txDriver); !ok { return nil, errors.New("ent: mutation is not running in a transaction") } @@ -3532,14 +3503,14 @@ func (m InternshipMutation) Tx() (*Tx, error) { } // SetID sets the value of the id field. Note that this -// operation is only accepted on creation of Internship entities. -func (m *InternshipMutation) SetID(id string) { +// operation is only accepted on creation of Discount entities. +func (m *DiscountMutation) SetID(id string) { m.id = &id } // ID returns the ID value in the mutation. Note that the ID is only available // if it was provided to the builder or after it was returned from the database. -func (m *InternshipMutation) ID() (id string, exists bool) { +func (m *DiscountMutation) ID() (id string, exists bool) { if m.id == nil { return } @@ -3550,7 +3521,7 @@ func (m *InternshipMutation) ID() (id string, exists bool) { // That means, if the mutation is applied within a transaction with an isolation level such // as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated // or updated by the mutation. -func (m *InternshipMutation) IDs(ctx context.Context) ([]string, error) { +func (m *DiscountMutation) IDs(ctx context.Context) ([]string, error) { switch { case m.op.Is(OpUpdateOne | OpDeleteOne): id, exists := m.ID() @@ -3559,19 +3530,19 @@ func (m *InternshipMutation) IDs(ctx context.Context) ([]string, error) { } fallthrough case m.op.Is(OpUpdate | OpDelete): - return m.Client().Internship.Query().Where(m.predicates...).IDs(ctx) + return m.Client().Discount.Query().Where(m.predicates...).IDs(ctx) default: return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) } } // SetStatus sets the "status" field. -func (m *InternshipMutation) SetStatus(s string) { +func (m *DiscountMutation) SetStatus(s string) { m.status = &s } // Status returns the value of the "status" field in the mutation. -func (m *InternshipMutation) Status() (r string, exists bool) { +func (m *DiscountMutation) Status() (r string, exists bool) { v := m.status if v == nil { return @@ -3579,10 +3550,10 @@ func (m *InternshipMutation) Status() (r string, exists bool) { return *v, true } -// OldStatus returns the old "status" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldStatus returns the old "status" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldStatus(ctx context.Context) (v string, err error) { +func (m *DiscountMutation) OldStatus(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldStatus is only allowed on UpdateOne operations") } @@ -3597,17 +3568,17 @@ func (m *InternshipMutation) OldStatus(ctx context.Context) (v string, err error } // ResetStatus resets all changes to the "status" field. -func (m *InternshipMutation) ResetStatus() { +func (m *DiscountMutation) ResetStatus() { m.status = nil } // SetCreatedAt sets the "created_at" field. -func (m *InternshipMutation) SetCreatedAt(t time.Time) { +func (m *DiscountMutation) SetCreatedAt(t time.Time) { m.created_at = &t } // CreatedAt returns the value of the "created_at" field in the mutation. -func (m *InternshipMutation) CreatedAt() (r time.Time, exists bool) { +func (m *DiscountMutation) CreatedAt() (r time.Time, exists bool) { v := m.created_at if v == nil { return @@ -3615,10 +3586,10 @@ func (m *InternshipMutation) CreatedAt() (r time.Time, exists bool) { return *v, true } -// OldCreatedAt returns the old "created_at" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedAt returns the old "created_at" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { +func (m *DiscountMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") } @@ -3633,17 +3604,17 @@ func (m *InternshipMutation) OldCreatedAt(ctx context.Context) (v time.Time, err } // ResetCreatedAt resets all changes to the "created_at" field. -func (m *InternshipMutation) ResetCreatedAt() { +func (m *DiscountMutation) ResetCreatedAt() { m.created_at = nil } // SetUpdatedAt sets the "updated_at" field. -func (m *InternshipMutation) SetUpdatedAt(t time.Time) { +func (m *DiscountMutation) SetUpdatedAt(t time.Time) { m.updated_at = &t } // UpdatedAt returns the value of the "updated_at" field in the mutation. -func (m *InternshipMutation) UpdatedAt() (r time.Time, exists bool) { +func (m *DiscountMutation) UpdatedAt() (r time.Time, exists bool) { v := m.updated_at if v == nil { return @@ -3651,10 +3622,10 @@ func (m *InternshipMutation) UpdatedAt() (r time.Time, exists bool) { return *v, true } -// OldUpdatedAt returns the old "updated_at" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedAt returns the old "updated_at" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { +func (m *DiscountMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") } @@ -3669,17 +3640,17 @@ func (m *InternshipMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err } // ResetUpdatedAt resets all changes to the "updated_at" field. -func (m *InternshipMutation) ResetUpdatedAt() { +func (m *DiscountMutation) ResetUpdatedAt() { m.updated_at = nil } // SetCreatedBy sets the "created_by" field. -func (m *InternshipMutation) SetCreatedBy(s string) { +func (m *DiscountMutation) SetCreatedBy(s string) { m.created_by = &s } // CreatedBy returns the value of the "created_by" field in the mutation. -func (m *InternshipMutation) CreatedBy() (r string, exists bool) { +func (m *DiscountMutation) CreatedBy() (r string, exists bool) { v := m.created_by if v == nil { return @@ -3687,10 +3658,10 @@ func (m *InternshipMutation) CreatedBy() (r string, exists bool) { return *v, true } -// OldCreatedBy returns the old "created_by" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedBy returns the old "created_by" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldCreatedBy(ctx context.Context) (v string, err error) { +func (m *DiscountMutation) OldCreatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") } @@ -3705,30 +3676,30 @@ func (m *InternshipMutation) OldCreatedBy(ctx context.Context) (v string, err er } // ClearCreatedBy clears the value of the "created_by" field. -func (m *InternshipMutation) ClearCreatedBy() { +func (m *DiscountMutation) ClearCreatedBy() { m.created_by = nil - m.clearedFields[internship.FieldCreatedBy] = struct{}{} + m.clearedFields[discount.FieldCreatedBy] = struct{}{} } // CreatedByCleared returns if the "created_by" field was cleared in this mutation. -func (m *InternshipMutation) CreatedByCleared() bool { - _, ok := m.clearedFields[internship.FieldCreatedBy] +func (m *DiscountMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[discount.FieldCreatedBy] return ok } // ResetCreatedBy resets all changes to the "created_by" field. -func (m *InternshipMutation) ResetCreatedBy() { +func (m *DiscountMutation) ResetCreatedBy() { m.created_by = nil - delete(m.clearedFields, internship.FieldCreatedBy) + delete(m.clearedFields, discount.FieldCreatedBy) } // SetUpdatedBy sets the "updated_by" field. -func (m *InternshipMutation) SetUpdatedBy(s string) { +func (m *DiscountMutation) SetUpdatedBy(s string) { m.updated_by = &s } // UpdatedBy returns the value of the "updated_by" field in the mutation. -func (m *InternshipMutation) UpdatedBy() (r string, exists bool) { +func (m *DiscountMutation) UpdatedBy() (r string, exists bool) { v := m.updated_by if v == nil { return @@ -3736,10 +3707,10 @@ func (m *InternshipMutation) UpdatedBy() (r string, exists bool) { return *v, true } -// OldUpdatedBy returns the old "updated_by" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedBy returns the old "updated_by" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { +func (m *DiscountMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") } @@ -3754,102 +3725,66 @@ func (m *InternshipMutation) OldUpdatedBy(ctx context.Context) (v string, err er } // ClearUpdatedBy clears the value of the "updated_by" field. -func (m *InternshipMutation) ClearUpdatedBy() { +func (m *DiscountMutation) ClearUpdatedBy() { m.updated_by = nil - m.clearedFields[internship.FieldUpdatedBy] = struct{}{} + m.clearedFields[discount.FieldUpdatedBy] = struct{}{} } // UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. -func (m *InternshipMutation) UpdatedByCleared() bool { - _, ok := m.clearedFields[internship.FieldUpdatedBy] +func (m *DiscountMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[discount.FieldUpdatedBy] return ok } // ResetUpdatedBy resets all changes to the "updated_by" field. -func (m *InternshipMutation) ResetUpdatedBy() { +func (m *DiscountMutation) ResetUpdatedBy() { m.updated_by = nil - delete(m.clearedFields, internship.FieldUpdatedBy) + delete(m.clearedFields, discount.FieldUpdatedBy) } -// SetTitle sets the "title" field. -func (m *InternshipMutation) SetTitle(s string) { - m.title = &s -} - -// Title returns the value of the "title" field in the mutation. -func (m *InternshipMutation) Title() (r string, exists bool) { - v := m.title - if v == nil { - return - } - return *v, true -} - -// OldTitle returns the old "title" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldTitle(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldTitle is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldTitle requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldTitle: %w", err) - } - return oldValue.Title, nil -} - -// ResetTitle resets all changes to the "title" field. -func (m *InternshipMutation) ResetTitle() { - m.title = nil -} - -// SetLookupKey sets the "lookup_key" field. -func (m *InternshipMutation) SetLookupKey(s string) { - m.lookup_key = &s +// SetCode sets the "code" field. +func (m *DiscountMutation) SetCode(s string) { + m.code = &s } -// LookupKey returns the value of the "lookup_key" field in the mutation. -func (m *InternshipMutation) LookupKey() (r string, exists bool) { - v := m.lookup_key +// Code returns the value of the "code" field in the mutation. +func (m *DiscountMutation) Code() (r string, exists bool) { + v := m.code if v == nil { return } return *v, true } -// OldLookupKey returns the old "lookup_key" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldCode returns the old "code" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldLookupKey(ctx context.Context) (v string, err error) { +func (m *DiscountMutation) OldCode(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLookupKey is only allowed on UpdateOne operations") + return v, errors.New("OldCode is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLookupKey requires an ID field in the mutation") + return v, errors.New("OldCode requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldLookupKey: %w", err) + return v, fmt.Errorf("querying old value for OldCode: %w", err) } - return oldValue.LookupKey, nil + return oldValue.Code, nil } -// ResetLookupKey resets all changes to the "lookup_key" field. -func (m *InternshipMutation) ResetLookupKey() { - m.lookup_key = nil +// ResetCode resets all changes to the "code" field. +func (m *DiscountMutation) ResetCode() { + m.code = nil } // SetDescription sets the "description" field. -func (m *InternshipMutation) SetDescription(s string) { +func (m *DiscountMutation) SetDescription(s string) { m.description = &s } // Description returns the value of the "description" field in the mutation. -func (m *InternshipMutation) Description() (r string, exists bool) { +func (m *DiscountMutation) Description() (r string, exists bool) { v := m.description if v == nil { return @@ -3857,10 +3792,10 @@ func (m *InternshipMutation) Description() (r string, exists bool) { return *v, true } -// OldDescription returns the old "description" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldDescription returns the old "description" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldDescription(ctx context.Context) (v string, err error) { +func (m *DiscountMutation) OldDescription(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldDescription is only allowed on UpdateOne operations") } @@ -3874,685 +3809,430 @@ func (m *InternshipMutation) OldDescription(ctx context.Context) (v string, err return oldValue.Description, nil } +// ClearDescription clears the value of the "description" field. +func (m *DiscountMutation) ClearDescription() { + m.description = nil + m.clearedFields[discount.FieldDescription] = struct{}{} +} + +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *DiscountMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[discount.FieldDescription] + return ok +} + // ResetDescription resets all changes to the "description" field. -func (m *InternshipMutation) ResetDescription() { +func (m *DiscountMutation) ResetDescription() { m.description = nil + delete(m.clearedFields, discount.FieldDescription) } -// SetSkills sets the "skills" field. -func (m *InternshipMutation) SetSkills(s []string) { - m.skills = &s - m.appendskills = nil +// SetDiscountType sets the "discount_type" field. +func (m *DiscountMutation) SetDiscountType(tt types.DiscountType) { + m.discount_type = &tt } -// Skills returns the value of the "skills" field in the mutation. -func (m *InternshipMutation) Skills() (r []string, exists bool) { - v := m.skills +// DiscountType returns the value of the "discount_type" field in the mutation. +func (m *DiscountMutation) DiscountType() (r types.DiscountType, exists bool) { + v := m.discount_type if v == nil { return } return *v, true } -// OldSkills returns the old "skills" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldDiscountType returns the old "discount_type" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldSkills(ctx context.Context) (v []string, err error) { +func (m *DiscountMutation) OldDiscountType(ctx context.Context) (v types.DiscountType, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldSkills is only allowed on UpdateOne operations") + return v, errors.New("OldDiscountType is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldSkills requires an ID field in the mutation") + return v, errors.New("OldDiscountType requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldSkills: %w", err) - } - return oldValue.Skills, nil -} - -// AppendSkills adds s to the "skills" field. -func (m *InternshipMutation) AppendSkills(s []string) { - m.appendskills = append(m.appendskills, s...) -} - -// AppendedSkills returns the list of values that were appended to the "skills" field in this mutation. -func (m *InternshipMutation) AppendedSkills() ([]string, bool) { - if len(m.appendskills) == 0 { - return nil, false + return v, fmt.Errorf("querying old value for OldDiscountType: %w", err) } - return m.appendskills, true -} - -// ClearSkills clears the value of the "skills" field. -func (m *InternshipMutation) ClearSkills() { - m.skills = nil - m.appendskills = nil - m.clearedFields[internship.FieldSkills] = struct{}{} -} - -// SkillsCleared returns if the "skills" field was cleared in this mutation. -func (m *InternshipMutation) SkillsCleared() bool { - _, ok := m.clearedFields[internship.FieldSkills] - return ok + return oldValue.DiscountType, nil } -// ResetSkills resets all changes to the "skills" field. -func (m *InternshipMutation) ResetSkills() { - m.skills = nil - m.appendskills = nil - delete(m.clearedFields, internship.FieldSkills) +// ResetDiscountType resets all changes to the "discount_type" field. +func (m *DiscountMutation) ResetDiscountType() { + m.discount_type = nil } -// SetLevel sets the "level" field. -func (m *InternshipMutation) SetLevel(s string) { - m.level = &s +// SetDiscountValue sets the "discount_value" field. +func (m *DiscountMutation) SetDiscountValue(d decimal.Decimal) { + m.discount_value = &d } -// Level returns the value of the "level" field in the mutation. -func (m *InternshipMutation) Level() (r string, exists bool) { - v := m.level +// DiscountValue returns the value of the "discount_value" field in the mutation. +func (m *DiscountMutation) DiscountValue() (r decimal.Decimal, exists bool) { + v := m.discount_value if v == nil { return } return *v, true } -// OldLevel returns the old "level" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldDiscountValue returns the old "discount_value" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldLevel(ctx context.Context) (v string, err error) { +func (m *DiscountMutation) OldDiscountValue(ctx context.Context) (v decimal.Decimal, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLevel is only allowed on UpdateOne operations") + return v, errors.New("OldDiscountValue is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLevel requires an ID field in the mutation") + return v, errors.New("OldDiscountValue requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldLevel: %w", err) + return v, fmt.Errorf("querying old value for OldDiscountValue: %w", err) } - return oldValue.Level, nil -} - -// ClearLevel clears the value of the "level" field. -func (m *InternshipMutation) ClearLevel() { - m.level = nil - m.clearedFields[internship.FieldLevel] = struct{}{} -} - -// LevelCleared returns if the "level" field was cleared in this mutation. -func (m *InternshipMutation) LevelCleared() bool { - _, ok := m.clearedFields[internship.FieldLevel] - return ok + return oldValue.DiscountValue, nil } -// ResetLevel resets all changes to the "level" field. -func (m *InternshipMutation) ResetLevel() { - m.level = nil - delete(m.clearedFields, internship.FieldLevel) +// ResetDiscountValue resets all changes to the "discount_value" field. +func (m *DiscountMutation) ResetDiscountValue() { + m.discount_value = nil } -// SetMode sets the "mode" field. -func (m *InternshipMutation) SetMode(s string) { - m.mode = &s +// SetValidFrom sets the "valid_from" field. +func (m *DiscountMutation) SetValidFrom(t time.Time) { + m.valid_from = &t } -// Mode returns the value of the "mode" field in the mutation. -func (m *InternshipMutation) Mode() (r string, exists bool) { - v := m.mode +// ValidFrom returns the value of the "valid_from" field in the mutation. +func (m *DiscountMutation) ValidFrom() (r time.Time, exists bool) { + v := m.valid_from if v == nil { return } return *v, true } -// OldMode returns the old "mode" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldValidFrom returns the old "valid_from" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldMode(ctx context.Context) (v string, err error) { +func (m *DiscountMutation) OldValidFrom(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldMode is only allowed on UpdateOne operations") + return v, errors.New("OldValidFrom is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldMode requires an ID field in the mutation") + return v, errors.New("OldValidFrom requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldMode: %w", err) + return v, fmt.Errorf("querying old value for OldValidFrom: %w", err) } - return oldValue.Mode, nil + return oldValue.ValidFrom, nil } -// ResetMode resets all changes to the "mode" field. -func (m *InternshipMutation) ResetMode() { - m.mode = nil +// ResetValidFrom resets all changes to the "valid_from" field. +func (m *DiscountMutation) ResetValidFrom() { + m.valid_from = nil } -// SetDurationInWeeks sets the "duration_in_weeks" field. -func (m *InternshipMutation) SetDurationInWeeks(i int) { - m.duration_in_weeks = &i - m.addduration_in_weeks = nil +// SetValidUntil sets the "valid_until" field. +func (m *DiscountMutation) SetValidUntil(t time.Time) { + m.valid_until = &t } -// DurationInWeeks returns the value of the "duration_in_weeks" field in the mutation. -func (m *InternshipMutation) DurationInWeeks() (r int, exists bool) { - v := m.duration_in_weeks +// ValidUntil returns the value of the "valid_until" field in the mutation. +func (m *DiscountMutation) ValidUntil() (r time.Time, exists bool) { + v := m.valid_until if v == nil { return } return *v, true } -// OldDurationInWeeks returns the old "duration_in_weeks" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldValidUntil returns the old "valid_until" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldDurationInWeeks(ctx context.Context) (v int, err error) { +func (m *DiscountMutation) OldValidUntil(ctx context.Context) (v *time.Time, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldDurationInWeeks is only allowed on UpdateOne operations") + return v, errors.New("OldValidUntil is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldDurationInWeeks requires an ID field in the mutation") + return v, errors.New("OldValidUntil requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldDurationInWeeks: %w", err) + return v, fmt.Errorf("querying old value for OldValidUntil: %w", err) } - return oldValue.DurationInWeeks, nil + return oldValue.ValidUntil, nil } -// AddDurationInWeeks adds i to the "duration_in_weeks" field. -func (m *InternshipMutation) AddDurationInWeeks(i int) { - if m.addduration_in_weeks != nil { - *m.addduration_in_weeks += i - } else { - m.addduration_in_weeks = &i - } -} - -// AddedDurationInWeeks returns the value that was added to the "duration_in_weeks" field in this mutation. -func (m *InternshipMutation) AddedDurationInWeeks() (r int, exists bool) { - v := m.addduration_in_weeks - if v == nil { - return - } - return *v, true -} - -// ClearDurationInWeeks clears the value of the "duration_in_weeks" field. -func (m *InternshipMutation) ClearDurationInWeeks() { - m.duration_in_weeks = nil - m.addduration_in_weeks = nil - m.clearedFields[internship.FieldDurationInWeeks] = struct{}{} +// ClearValidUntil clears the value of the "valid_until" field. +func (m *DiscountMutation) ClearValidUntil() { + m.valid_until = nil + m.clearedFields[discount.FieldValidUntil] = struct{}{} } -// DurationInWeeksCleared returns if the "duration_in_weeks" field was cleared in this mutation. -func (m *InternshipMutation) DurationInWeeksCleared() bool { - _, ok := m.clearedFields[internship.FieldDurationInWeeks] +// ValidUntilCleared returns if the "valid_until" field was cleared in this mutation. +func (m *DiscountMutation) ValidUntilCleared() bool { + _, ok := m.clearedFields[discount.FieldValidUntil] return ok } -// ResetDurationInWeeks resets all changes to the "duration_in_weeks" field. -func (m *InternshipMutation) ResetDurationInWeeks() { - m.duration_in_weeks = nil - m.addduration_in_weeks = nil - delete(m.clearedFields, internship.FieldDurationInWeeks) +// ResetValidUntil resets all changes to the "valid_until" field. +func (m *DiscountMutation) ResetValidUntil() { + m.valid_until = nil + delete(m.clearedFields, discount.FieldValidUntil) } -// SetLearningOutcomes sets the "learning_outcomes" field. -func (m *InternshipMutation) SetLearningOutcomes(s []string) { - m.learning_outcomes = &s - m.appendlearning_outcomes = nil +// SetIsActive sets the "is_active" field. +func (m *DiscountMutation) SetIsActive(b bool) { + m.is_active = &b } -// LearningOutcomes returns the value of the "learning_outcomes" field in the mutation. -func (m *InternshipMutation) LearningOutcomes() (r []string, exists bool) { - v := m.learning_outcomes +// IsActive returns the value of the "is_active" field in the mutation. +func (m *DiscountMutation) IsActive() (r bool, exists bool) { + v := m.is_active if v == nil { return } return *v, true } -// OldLearningOutcomes returns the old "learning_outcomes" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldIsActive returns the old "is_active" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldLearningOutcomes(ctx context.Context) (v []string, err error) { +func (m *DiscountMutation) OldIsActive(ctx context.Context) (v bool, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLearningOutcomes is only allowed on UpdateOne operations") + return v, errors.New("OldIsActive is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLearningOutcomes requires an ID field in the mutation") + return v, errors.New("OldIsActive requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldLearningOutcomes: %w", err) - } - return oldValue.LearningOutcomes, nil -} - -// AppendLearningOutcomes adds s to the "learning_outcomes" field. -func (m *InternshipMutation) AppendLearningOutcomes(s []string) { - m.appendlearning_outcomes = append(m.appendlearning_outcomes, s...) -} - -// AppendedLearningOutcomes returns the list of values that were appended to the "learning_outcomes" field in this mutation. -func (m *InternshipMutation) AppendedLearningOutcomes() ([]string, bool) { - if len(m.appendlearning_outcomes) == 0 { - return nil, false + return v, fmt.Errorf("querying old value for OldIsActive: %w", err) } - return m.appendlearning_outcomes, true -} - -// ClearLearningOutcomes clears the value of the "learning_outcomes" field. -func (m *InternshipMutation) ClearLearningOutcomes() { - m.learning_outcomes = nil - m.appendlearning_outcomes = nil - m.clearedFields[internship.FieldLearningOutcomes] = struct{}{} -} - -// LearningOutcomesCleared returns if the "learning_outcomes" field was cleared in this mutation. -func (m *InternshipMutation) LearningOutcomesCleared() bool { - _, ok := m.clearedFields[internship.FieldLearningOutcomes] - return ok + return oldValue.IsActive, nil } -// ResetLearningOutcomes resets all changes to the "learning_outcomes" field. -func (m *InternshipMutation) ResetLearningOutcomes() { - m.learning_outcomes = nil - m.appendlearning_outcomes = nil - delete(m.clearedFields, internship.FieldLearningOutcomes) +// ResetIsActive resets all changes to the "is_active" field. +func (m *DiscountMutation) ResetIsActive() { + m.is_active = nil } -// SetPrerequisites sets the "prerequisites" field. -func (m *InternshipMutation) SetPrerequisites(s []string) { - m.prerequisites = &s - m.appendprerequisites = nil +// SetMaxUses sets the "max_uses" field. +func (m *DiscountMutation) SetMaxUses(i int) { + m.max_uses = &i + m.addmax_uses = nil } -// Prerequisites returns the value of the "prerequisites" field in the mutation. -func (m *InternshipMutation) Prerequisites() (r []string, exists bool) { - v := m.prerequisites +// MaxUses returns the value of the "max_uses" field in the mutation. +func (m *DiscountMutation) MaxUses() (r int, exists bool) { + v := m.max_uses if v == nil { return } return *v, true } -// OldPrerequisites returns the old "prerequisites" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldMaxUses returns the old "max_uses" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldPrerequisites(ctx context.Context) (v []string, err error) { +func (m *DiscountMutation) OldMaxUses(ctx context.Context) (v *int, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPrerequisites is only allowed on UpdateOne operations") + return v, errors.New("OldMaxUses is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPrerequisites requires an ID field in the mutation") + return v, errors.New("OldMaxUses requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldPrerequisites: %w", err) + return v, fmt.Errorf("querying old value for OldMaxUses: %w", err) } - return oldValue.Prerequisites, nil + return oldValue.MaxUses, nil } -// AppendPrerequisites adds s to the "prerequisites" field. -func (m *InternshipMutation) AppendPrerequisites(s []string) { - m.appendprerequisites = append(m.appendprerequisites, s...) +// AddMaxUses adds i to the "max_uses" field. +func (m *DiscountMutation) AddMaxUses(i int) { + if m.addmax_uses != nil { + *m.addmax_uses += i + } else { + m.addmax_uses = &i + } } -// AppendedPrerequisites returns the list of values that were appended to the "prerequisites" field in this mutation. -func (m *InternshipMutation) AppendedPrerequisites() ([]string, bool) { - if len(m.appendprerequisites) == 0 { - return nil, false +// AddedMaxUses returns the value that was added to the "max_uses" field in this mutation. +func (m *DiscountMutation) AddedMaxUses() (r int, exists bool) { + v := m.addmax_uses + if v == nil { + return } - return m.appendprerequisites, true + return *v, true } -// ClearPrerequisites clears the value of the "prerequisites" field. -func (m *InternshipMutation) ClearPrerequisites() { - m.prerequisites = nil - m.appendprerequisites = nil - m.clearedFields[internship.FieldPrerequisites] = struct{}{} +// ClearMaxUses clears the value of the "max_uses" field. +func (m *DiscountMutation) ClearMaxUses() { + m.max_uses = nil + m.addmax_uses = nil + m.clearedFields[discount.FieldMaxUses] = struct{}{} } -// PrerequisitesCleared returns if the "prerequisites" field was cleared in this mutation. -func (m *InternshipMutation) PrerequisitesCleared() bool { - _, ok := m.clearedFields[internship.FieldPrerequisites] +// MaxUsesCleared returns if the "max_uses" field was cleared in this mutation. +func (m *DiscountMutation) MaxUsesCleared() bool { + _, ok := m.clearedFields[discount.FieldMaxUses] return ok } -// ResetPrerequisites resets all changes to the "prerequisites" field. -func (m *InternshipMutation) ResetPrerequisites() { - m.prerequisites = nil - m.appendprerequisites = nil - delete(m.clearedFields, internship.FieldPrerequisites) +// ResetMaxUses resets all changes to the "max_uses" field. +func (m *DiscountMutation) ResetMaxUses() { + m.max_uses = nil + m.addmax_uses = nil + delete(m.clearedFields, discount.FieldMaxUses) } -// SetBenefits sets the "benefits" field. -func (m *InternshipMutation) SetBenefits(s []string) { - m.benefits = &s - m.appendbenefits = nil +// SetMinOrderValue sets the "min_order_value" field. +func (m *DiscountMutation) SetMinOrderValue(d decimal.Decimal) { + m.min_order_value = &d } -// Benefits returns the value of the "benefits" field in the mutation. -func (m *InternshipMutation) Benefits() (r []string, exists bool) { - v := m.benefits +// MinOrderValue returns the value of the "min_order_value" field in the mutation. +func (m *DiscountMutation) MinOrderValue() (r decimal.Decimal, exists bool) { + v := m.min_order_value if v == nil { return } return *v, true } -// OldBenefits returns the old "benefits" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldMinOrderValue returns the old "min_order_value" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldBenefits(ctx context.Context) (v []string, err error) { +func (m *DiscountMutation) OldMinOrderValue(ctx context.Context) (v *decimal.Decimal, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldBenefits is only allowed on UpdateOne operations") + return v, errors.New("OldMinOrderValue is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldBenefits requires an ID field in the mutation") + return v, errors.New("OldMinOrderValue requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldBenefits: %w", err) - } - return oldValue.Benefits, nil -} - -// AppendBenefits adds s to the "benefits" field. -func (m *InternshipMutation) AppendBenefits(s []string) { - m.appendbenefits = append(m.appendbenefits, s...) -} - -// AppendedBenefits returns the list of values that were appended to the "benefits" field in this mutation. -func (m *InternshipMutation) AppendedBenefits() ([]string, bool) { - if len(m.appendbenefits) == 0 { - return nil, false + return v, fmt.Errorf("querying old value for OldMinOrderValue: %w", err) } - return m.appendbenefits, true + return oldValue.MinOrderValue, nil } -// ClearBenefits clears the value of the "benefits" field. -func (m *InternshipMutation) ClearBenefits() { - m.benefits = nil - m.appendbenefits = nil - m.clearedFields[internship.FieldBenefits] = struct{}{} +// ClearMinOrderValue clears the value of the "min_order_value" field. +func (m *DiscountMutation) ClearMinOrderValue() { + m.min_order_value = nil + m.clearedFields[discount.FieldMinOrderValue] = struct{}{} } -// BenefitsCleared returns if the "benefits" field was cleared in this mutation. -func (m *InternshipMutation) BenefitsCleared() bool { - _, ok := m.clearedFields[internship.FieldBenefits] +// MinOrderValueCleared returns if the "min_order_value" field was cleared in this mutation. +func (m *DiscountMutation) MinOrderValueCleared() bool { + _, ok := m.clearedFields[discount.FieldMinOrderValue] return ok } -// ResetBenefits resets all changes to the "benefits" field. -func (m *InternshipMutation) ResetBenefits() { - m.benefits = nil - m.appendbenefits = nil - delete(m.clearedFields, internship.FieldBenefits) +// ResetMinOrderValue resets all changes to the "min_order_value" field. +func (m *DiscountMutation) ResetMinOrderValue() { + m.min_order_value = nil + delete(m.clearedFields, discount.FieldMinOrderValue) } -// SetCurrency sets the "currency" field. -func (m *InternshipMutation) SetCurrency(s string) { - m.currency = &s +// SetIsCombinable sets the "is_combinable" field. +func (m *DiscountMutation) SetIsCombinable(b bool) { + m.is_combinable = &b } -// Currency returns the value of the "currency" field in the mutation. -func (m *InternshipMutation) Currency() (r string, exists bool) { - v := m.currency +// IsCombinable returns the value of the "is_combinable" field in the mutation. +func (m *DiscountMutation) IsCombinable() (r bool, exists bool) { + v := m.is_combinable if v == nil { return } return *v, true } -// OldCurrency returns the old "currency" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldCurrency(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCurrency is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCurrency requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldCurrency: %w", err) - } - return oldValue.Currency, nil -} - -// ClearCurrency clears the value of the "currency" field. -func (m *InternshipMutation) ClearCurrency() { - m.currency = nil - m.clearedFields[internship.FieldCurrency] = struct{}{} -} - -// CurrencyCleared returns if the "currency" field was cleared in this mutation. -func (m *InternshipMutation) CurrencyCleared() bool { - _, ok := m.clearedFields[internship.FieldCurrency] - return ok -} - -// ResetCurrency resets all changes to the "currency" field. -func (m *InternshipMutation) ResetCurrency() { - m.currency = nil - delete(m.clearedFields, internship.FieldCurrency) -} - -// SetPrice sets the "price" field. -func (m *InternshipMutation) SetPrice(d decimal.Decimal) { - m.price = &d -} - -// Price returns the value of the "price" field in the mutation. -func (m *InternshipMutation) Price() (r decimal.Decimal, exists bool) { - v := m.price - if v == nil { - return - } - return *v, true -} - -// OldPrice returns the old "price" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldPrice(ctx context.Context) (v decimal.Decimal, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPrice is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPrice requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldPrice: %w", err) - } - return oldValue.Price, nil -} - -// ClearPrice clears the value of the "price" field. -func (m *InternshipMutation) ClearPrice() { - m.price = nil - m.clearedFields[internship.FieldPrice] = struct{}{} -} - -// PriceCleared returns if the "price" field was cleared in this mutation. -func (m *InternshipMutation) PriceCleared() bool { - _, ok := m.clearedFields[internship.FieldPrice] - return ok -} - -// ResetPrice resets all changes to the "price" field. -func (m *InternshipMutation) ResetPrice() { - m.price = nil - delete(m.clearedFields, internship.FieldPrice) -} - -// SetFlatDiscount sets the "flat_discount" field. -func (m *InternshipMutation) SetFlatDiscount(d decimal.Decimal) { - m.flat_discount = &d -} - -// FlatDiscount returns the value of the "flat_discount" field in the mutation. -func (m *InternshipMutation) FlatDiscount() (r decimal.Decimal, exists bool) { - v := m.flat_discount - if v == nil { - return - } - return *v, true -} - -// OldFlatDiscount returns the old "flat_discount" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldIsCombinable returns the old "is_combinable" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldFlatDiscount(ctx context.Context) (v decimal.Decimal, err error) { +func (m *DiscountMutation) OldIsCombinable(ctx context.Context) (v bool, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldFlatDiscount is only allowed on UpdateOne operations") + return v, errors.New("OldIsCombinable is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldFlatDiscount requires an ID field in the mutation") + return v, errors.New("OldIsCombinable requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldFlatDiscount: %w", err) + return v, fmt.Errorf("querying old value for OldIsCombinable: %w", err) } - return oldValue.FlatDiscount, nil -} - -// ClearFlatDiscount clears the value of the "flat_discount" field. -func (m *InternshipMutation) ClearFlatDiscount() { - m.flat_discount = nil - m.clearedFields[internship.FieldFlatDiscount] = struct{}{} -} - -// FlatDiscountCleared returns if the "flat_discount" field was cleared in this mutation. -func (m *InternshipMutation) FlatDiscountCleared() bool { - _, ok := m.clearedFields[internship.FieldFlatDiscount] - return ok + return oldValue.IsCombinable, nil } -// ResetFlatDiscount resets all changes to the "flat_discount" field. -func (m *InternshipMutation) ResetFlatDiscount() { - m.flat_discount = nil - delete(m.clearedFields, internship.FieldFlatDiscount) +// ResetIsCombinable resets all changes to the "is_combinable" field. +func (m *DiscountMutation) ResetIsCombinable() { + m.is_combinable = nil } -// SetPercentageDiscount sets the "percentage_discount" field. -func (m *InternshipMutation) SetPercentageDiscount(d decimal.Decimal) { - m.percentage_discount = &d +// SetMetadata sets the "metadata" field. +func (m *DiscountMutation) SetMetadata(value map[string]string) { + m.metadata = &value } -// PercentageDiscount returns the value of the "percentage_discount" field in the mutation. -func (m *InternshipMutation) PercentageDiscount() (r decimal.Decimal, exists bool) { - v := m.percentage_discount +// Metadata returns the value of the "metadata" field in the mutation. +func (m *DiscountMutation) Metadata() (r map[string]string, exists bool) { + v := m.metadata if v == nil { return } return *v, true } -// OldPercentageDiscount returns the old "percentage_discount" field's value of the Internship entity. -// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// OldMetadata returns the old "metadata" field's value of the Discount entity. +// If the Discount object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *InternshipMutation) OldPercentageDiscount(ctx context.Context) (v decimal.Decimal, err error) { +func (m *DiscountMutation) OldMetadata(ctx context.Context) (v map[string]string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPercentageDiscount is only allowed on UpdateOne operations") + return v, errors.New("OldMetadata is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPercentageDiscount requires an ID field in the mutation") + return v, errors.New("OldMetadata requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldPercentageDiscount: %w", err) + return v, fmt.Errorf("querying old value for OldMetadata: %w", err) } - return oldValue.PercentageDiscount, nil + return oldValue.Metadata, nil } -// ClearPercentageDiscount clears the value of the "percentage_discount" field. -func (m *InternshipMutation) ClearPercentageDiscount() { - m.percentage_discount = nil - m.clearedFields[internship.FieldPercentageDiscount] = struct{}{} +// ClearMetadata clears the value of the "metadata" field. +func (m *DiscountMutation) ClearMetadata() { + m.metadata = nil + m.clearedFields[discount.FieldMetadata] = struct{}{} } -// PercentageDiscountCleared returns if the "percentage_discount" field was cleared in this mutation. -func (m *InternshipMutation) PercentageDiscountCleared() bool { - _, ok := m.clearedFields[internship.FieldPercentageDiscount] +// MetadataCleared returns if the "metadata" field was cleared in this mutation. +func (m *DiscountMutation) MetadataCleared() bool { + _, ok := m.clearedFields[discount.FieldMetadata] return ok } -// ResetPercentageDiscount resets all changes to the "percentage_discount" field. -func (m *InternshipMutation) ResetPercentageDiscount() { - m.percentage_discount = nil - delete(m.clearedFields, internship.FieldPercentageDiscount) -} - -// AddCategoryIDs adds the "categories" edge to the Category entity by ids. -func (m *InternshipMutation) AddCategoryIDs(ids ...string) { - if m.categories == nil { - m.categories = make(map[string]struct{}) - } - for i := range ids { - m.categories[ids[i]] = struct{}{} - } -} - -// ClearCategories clears the "categories" edge to the Category entity. -func (m *InternshipMutation) ClearCategories() { - m.clearedcategories = true -} - -// CategoriesCleared reports if the "categories" edge to the Category entity was cleared. -func (m *InternshipMutation) CategoriesCleared() bool { - return m.clearedcategories -} - -// RemoveCategoryIDs removes the "categories" edge to the Category entity by IDs. -func (m *InternshipMutation) RemoveCategoryIDs(ids ...string) { - if m.removedcategories == nil { - m.removedcategories = make(map[string]struct{}) - } - for i := range ids { - delete(m.categories, ids[i]) - m.removedcategories[ids[i]] = struct{}{} - } -} - -// RemovedCategories returns the removed IDs of the "categories" edge to the Category entity. -func (m *InternshipMutation) RemovedCategoriesIDs() (ids []string) { - for id := range m.removedcategories { - ids = append(ids, id) - } - return -} - -// CategoriesIDs returns the "categories" edge IDs in the mutation. -func (m *InternshipMutation) CategoriesIDs() (ids []string) { - for id := range m.categories { - ids = append(ids, id) - } - return -} - -// ResetCategories resets all changes to the "categories" edge. -func (m *InternshipMutation) ResetCategories() { - m.categories = nil - m.clearedcategories = false - m.removedcategories = nil +// ResetMetadata resets all changes to the "metadata" field. +func (m *DiscountMutation) ResetMetadata() { + m.metadata = nil + delete(m.clearedFields, discount.FieldMetadata) } -// Where appends a list predicates to the InternshipMutation builder. -func (m *InternshipMutation) Where(ps ...predicate.Internship) { +// Where appends a list predicates to the DiscountMutation builder. +func (m *DiscountMutation) Where(ps ...predicate.Discount) { m.predicates = append(m.predicates, ps...) } -// WhereP appends storage-level predicates to the InternshipMutation builder. Using this method, +// WhereP appends storage-level predicates to the DiscountMutation builder. Using this method, // users can use type-assertion to append predicates that do not depend on any generated package. -func (m *InternshipMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.Internship, len(ps)) +func (m *DiscountMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Discount, len(ps)) for i := range ps { p[i] = ps[i] } @@ -4560,81 +4240,72 @@ func (m *InternshipMutation) WhereP(ps ...func(*sql.Selector)) { } // Op returns the operation name. -func (m *InternshipMutation) Op() Op { +func (m *DiscountMutation) Op() Op { return m.op } // SetOp allows setting the mutation operation. -func (m *InternshipMutation) SetOp(op Op) { +func (m *DiscountMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (Internship). -func (m *InternshipMutation) Type() string { +// Type returns the node type of this mutation (Discount). +func (m *DiscountMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *InternshipMutation) Fields() []string { - fields := make([]string, 0, 19) +func (m *DiscountMutation) Fields() []string { + fields := make([]string, 0, 16) if m.status != nil { - fields = append(fields, internship.FieldStatus) + fields = append(fields, discount.FieldStatus) } if m.created_at != nil { - fields = append(fields, internship.FieldCreatedAt) + fields = append(fields, discount.FieldCreatedAt) } if m.updated_at != nil { - fields = append(fields, internship.FieldUpdatedAt) + fields = append(fields, discount.FieldUpdatedAt) } if m.created_by != nil { - fields = append(fields, internship.FieldCreatedBy) + fields = append(fields, discount.FieldCreatedBy) } if m.updated_by != nil { - fields = append(fields, internship.FieldUpdatedBy) - } - if m.title != nil { - fields = append(fields, internship.FieldTitle) + fields = append(fields, discount.FieldUpdatedBy) } - if m.lookup_key != nil { - fields = append(fields, internship.FieldLookupKey) + if m.code != nil { + fields = append(fields, discount.FieldCode) } if m.description != nil { - fields = append(fields, internship.FieldDescription) - } - if m.skills != nil { - fields = append(fields, internship.FieldSkills) - } - if m.level != nil { - fields = append(fields, internship.FieldLevel) + fields = append(fields, discount.FieldDescription) } - if m.mode != nil { - fields = append(fields, internship.FieldMode) + if m.discount_type != nil { + fields = append(fields, discount.FieldDiscountType) } - if m.duration_in_weeks != nil { - fields = append(fields, internship.FieldDurationInWeeks) + if m.discount_value != nil { + fields = append(fields, discount.FieldDiscountValue) } - if m.learning_outcomes != nil { - fields = append(fields, internship.FieldLearningOutcomes) + if m.valid_from != nil { + fields = append(fields, discount.FieldValidFrom) } - if m.prerequisites != nil { - fields = append(fields, internship.FieldPrerequisites) + if m.valid_until != nil { + fields = append(fields, discount.FieldValidUntil) } - if m.benefits != nil { - fields = append(fields, internship.FieldBenefits) + if m.is_active != nil { + fields = append(fields, discount.FieldIsActive) } - if m.currency != nil { - fields = append(fields, internship.FieldCurrency) + if m.max_uses != nil { + fields = append(fields, discount.FieldMaxUses) } - if m.price != nil { - fields = append(fields, internship.FieldPrice) + if m.min_order_value != nil { + fields = append(fields, discount.FieldMinOrderValue) } - if m.flat_discount != nil { - fields = append(fields, internship.FieldFlatDiscount) + if m.is_combinable != nil { + fields = append(fields, discount.FieldIsCombinable) } - if m.percentage_discount != nil { - fields = append(fields, internship.FieldPercentageDiscount) + if m.metadata != nil { + fields = append(fields, discount.FieldMetadata) } return fields } @@ -4642,46 +4313,40 @@ func (m *InternshipMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *InternshipMutation) Field(name string) (ent.Value, bool) { +func (m *DiscountMutation) Field(name string) (ent.Value, bool) { switch name { - case internship.FieldStatus: + case discount.FieldStatus: return m.Status() - case internship.FieldCreatedAt: + case discount.FieldCreatedAt: return m.CreatedAt() - case internship.FieldUpdatedAt: + case discount.FieldUpdatedAt: return m.UpdatedAt() - case internship.FieldCreatedBy: + case discount.FieldCreatedBy: return m.CreatedBy() - case internship.FieldUpdatedBy: + case discount.FieldUpdatedBy: return m.UpdatedBy() - case internship.FieldTitle: - return m.Title() - case internship.FieldLookupKey: - return m.LookupKey() - case internship.FieldDescription: + case discount.FieldCode: + return m.Code() + case discount.FieldDescription: return m.Description() - case internship.FieldSkills: - return m.Skills() - case internship.FieldLevel: - return m.Level() - case internship.FieldMode: - return m.Mode() - case internship.FieldDurationInWeeks: - return m.DurationInWeeks() - case internship.FieldLearningOutcomes: - return m.LearningOutcomes() - case internship.FieldPrerequisites: - return m.Prerequisites() - case internship.FieldBenefits: - return m.Benefits() - case internship.FieldCurrency: - return m.Currency() - case internship.FieldPrice: - return m.Price() - case internship.FieldFlatDiscount: - return m.FlatDiscount() - case internship.FieldPercentageDiscount: - return m.PercentageDiscount() + case discount.FieldDiscountType: + return m.DiscountType() + case discount.FieldDiscountValue: + return m.DiscountValue() + case discount.FieldValidFrom: + return m.ValidFrom() + case discount.FieldValidUntil: + return m.ValidUntil() + case discount.FieldIsActive: + return m.IsActive() + case discount.FieldMaxUses: + return m.MaxUses() + case discount.FieldMinOrderValue: + return m.MinOrderValue() + case discount.FieldIsCombinable: + return m.IsCombinable() + case discount.FieldMetadata: + return m.Metadata() } return nil, false } @@ -4689,198 +4354,171 @@ func (m *InternshipMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *InternshipMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *DiscountMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case internship.FieldStatus: + case discount.FieldStatus: return m.OldStatus(ctx) - case internship.FieldCreatedAt: + case discount.FieldCreatedAt: return m.OldCreatedAt(ctx) - case internship.FieldUpdatedAt: + case discount.FieldUpdatedAt: return m.OldUpdatedAt(ctx) - case internship.FieldCreatedBy: + case discount.FieldCreatedBy: return m.OldCreatedBy(ctx) - case internship.FieldUpdatedBy: + case discount.FieldUpdatedBy: return m.OldUpdatedBy(ctx) - case internship.FieldTitle: - return m.OldTitle(ctx) - case internship.FieldLookupKey: - return m.OldLookupKey(ctx) - case internship.FieldDescription: + case discount.FieldCode: + return m.OldCode(ctx) + case discount.FieldDescription: return m.OldDescription(ctx) - case internship.FieldSkills: - return m.OldSkills(ctx) - case internship.FieldLevel: - return m.OldLevel(ctx) - case internship.FieldMode: - return m.OldMode(ctx) - case internship.FieldDurationInWeeks: - return m.OldDurationInWeeks(ctx) - case internship.FieldLearningOutcomes: - return m.OldLearningOutcomes(ctx) - case internship.FieldPrerequisites: - return m.OldPrerequisites(ctx) - case internship.FieldBenefits: - return m.OldBenefits(ctx) - case internship.FieldCurrency: - return m.OldCurrency(ctx) - case internship.FieldPrice: - return m.OldPrice(ctx) - case internship.FieldFlatDiscount: - return m.OldFlatDiscount(ctx) - case internship.FieldPercentageDiscount: - return m.OldPercentageDiscount(ctx) + case discount.FieldDiscountType: + return m.OldDiscountType(ctx) + case discount.FieldDiscountValue: + return m.OldDiscountValue(ctx) + case discount.FieldValidFrom: + return m.OldValidFrom(ctx) + case discount.FieldValidUntil: + return m.OldValidUntil(ctx) + case discount.FieldIsActive: + return m.OldIsActive(ctx) + case discount.FieldMaxUses: + return m.OldMaxUses(ctx) + case discount.FieldMinOrderValue: + return m.OldMinOrderValue(ctx) + case discount.FieldIsCombinable: + return m.OldIsCombinable(ctx) + case discount.FieldMetadata: + return m.OldMetadata(ctx) } - return nil, fmt.Errorf("unknown Internship field %s", name) + return nil, fmt.Errorf("unknown Discount field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *InternshipMutation) SetField(name string, value ent.Value) error { +func (m *DiscountMutation) SetField(name string, value ent.Value) error { switch name { - case internship.FieldStatus: + case discount.FieldStatus: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetStatus(v) return nil - case internship.FieldCreatedAt: + case discount.FieldCreatedAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedAt(v) return nil - case internship.FieldUpdatedAt: + case discount.FieldUpdatedAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedAt(v) return nil - case internship.FieldCreatedBy: + case discount.FieldCreatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedBy(v) return nil - case internship.FieldUpdatedBy: + case discount.FieldUpdatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedBy(v) return nil - case internship.FieldTitle: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetTitle(v) - return nil - case internship.FieldLookupKey: + case discount.FieldCode: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetLookupKey(v) + m.SetCode(v) return nil - case internship.FieldDescription: + case discount.FieldDescription: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetDescription(v) return nil - case internship.FieldSkills: - v, ok := value.([]string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetSkills(v) - return nil - case internship.FieldLevel: - v, ok := value.(string) + case discount.FieldDiscountType: + v, ok := value.(types.DiscountType) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetLevel(v) + m.SetDiscountType(v) return nil - case internship.FieldMode: - v, ok := value.(string) + case discount.FieldDiscountValue: + v, ok := value.(decimal.Decimal) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetMode(v) + m.SetDiscountValue(v) return nil - case internship.FieldDurationInWeeks: - v, ok := value.(int) + case discount.FieldValidFrom: + v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDurationInWeeks(v) + m.SetValidFrom(v) return nil - case internship.FieldLearningOutcomes: - v, ok := value.([]string) + case discount.FieldValidUntil: + v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetLearningOutcomes(v) + m.SetValidUntil(v) return nil - case internship.FieldPrerequisites: - v, ok := value.([]string) + case discount.FieldIsActive: + v, ok := value.(bool) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetPrerequisites(v) + m.SetIsActive(v) return nil - case internship.FieldBenefits: - v, ok := value.([]string) + case discount.FieldMaxUses: + v, ok := value.(int) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetBenefits(v) + m.SetMaxUses(v) return nil - case internship.FieldCurrency: - v, ok := value.(string) + case discount.FieldMinOrderValue: + v, ok := value.(decimal.Decimal) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetCurrency(v) + m.SetMinOrderValue(v) return nil - case internship.FieldPrice: - v, ok := value.(decimal.Decimal) + case discount.FieldIsCombinable: + v, ok := value.(bool) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetPrice(v) + m.SetIsCombinable(v) return nil - case internship.FieldFlatDiscount: - v, ok := value.(decimal.Decimal) + case discount.FieldMetadata: + v, ok := value.(map[string]string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetFlatDiscount(v) - return nil - case internship.FieldPercentageDiscount: - v, ok := value.(decimal.Decimal) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetPercentageDiscount(v) + m.SetMetadata(v) return nil } - return fmt.Errorf("unknown Internship field %s", name) + return fmt.Errorf("unknown Discount field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *InternshipMutation) AddedFields() []string { +func (m *DiscountMutation) AddedFields() []string { var fields []string - if m.addduration_in_weeks != nil { - fields = append(fields, internship.FieldDurationInWeeks) + if m.addmax_uses != nil { + fields = append(fields, discount.FieldMaxUses) } return fields } @@ -4888,10 +4526,10 @@ func (m *InternshipMutation) AddedFields() []string { // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *InternshipMutation) AddedField(name string) (ent.Value, bool) { +func (m *DiscountMutation) AddedField(name string) (ent.Value, bool) { switch name { - case internship.FieldDurationInWeeks: - return m.AddedDurationInWeeks() + case discount.FieldMaxUses: + return m.AddedMaxUses() } return nil, false } @@ -4899,260 +4537,5887 @@ func (m *InternshipMutation) AddedField(name string) (ent.Value, bool) { // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *InternshipMutation) AddField(name string, value ent.Value) error { +func (m *DiscountMutation) AddField(name string, value ent.Value) error { switch name { - case internship.FieldDurationInWeeks: + case discount.FieldMaxUses: v, ok := value.(int) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.AddDurationInWeeks(v) + m.AddMaxUses(v) return nil } - return fmt.Errorf("unknown Internship numeric field %s", name) + return fmt.Errorf("unknown Discount numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *InternshipMutation) ClearedFields() []string { +func (m *DiscountMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(internship.FieldCreatedBy) { - fields = append(fields, internship.FieldCreatedBy) + if m.FieldCleared(discount.FieldCreatedBy) { + fields = append(fields, discount.FieldCreatedBy) } - if m.FieldCleared(internship.FieldUpdatedBy) { - fields = append(fields, internship.FieldUpdatedBy) + if m.FieldCleared(discount.FieldUpdatedBy) { + fields = append(fields, discount.FieldUpdatedBy) } - if m.FieldCleared(internship.FieldSkills) { - fields = append(fields, internship.FieldSkills) + if m.FieldCleared(discount.FieldDescription) { + fields = append(fields, discount.FieldDescription) } - if m.FieldCleared(internship.FieldLevel) { - fields = append(fields, internship.FieldLevel) + if m.FieldCleared(discount.FieldValidUntil) { + fields = append(fields, discount.FieldValidUntil) } - if m.FieldCleared(internship.FieldDurationInWeeks) { - fields = append(fields, internship.FieldDurationInWeeks) + if m.FieldCleared(discount.FieldMaxUses) { + fields = append(fields, discount.FieldMaxUses) } - if m.FieldCleared(internship.FieldLearningOutcomes) { - fields = append(fields, internship.FieldLearningOutcomes) + if m.FieldCleared(discount.FieldMinOrderValue) { + fields = append(fields, discount.FieldMinOrderValue) } - if m.FieldCleared(internship.FieldPrerequisites) { - fields = append(fields, internship.FieldPrerequisites) + if m.FieldCleared(discount.FieldMetadata) { + fields = append(fields, discount.FieldMetadata) } - if m.FieldCleared(internship.FieldBenefits) { - fields = append(fields, internship.FieldBenefits) + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *DiscountMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *DiscountMutation) ClearField(name string) error { + switch name { + case discount.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case discount.FieldUpdatedBy: + m.ClearUpdatedBy() + return nil + case discount.FieldDescription: + m.ClearDescription() + return nil + case discount.FieldValidUntil: + m.ClearValidUntil() + return nil + case discount.FieldMaxUses: + m.ClearMaxUses() + return nil + case discount.FieldMinOrderValue: + m.ClearMinOrderValue() + return nil + case discount.FieldMetadata: + m.ClearMetadata() + return nil } - if m.FieldCleared(internship.FieldCurrency) { - fields = append(fields, internship.FieldCurrency) + return fmt.Errorf("unknown Discount nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *DiscountMutation) ResetField(name string) error { + switch name { + case discount.FieldStatus: + m.ResetStatus() + return nil + case discount.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case discount.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case discount.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case discount.FieldUpdatedBy: + m.ResetUpdatedBy() + return nil + case discount.FieldCode: + m.ResetCode() + return nil + case discount.FieldDescription: + m.ResetDescription() + return nil + case discount.FieldDiscountType: + m.ResetDiscountType() + return nil + case discount.FieldDiscountValue: + m.ResetDiscountValue() + return nil + case discount.FieldValidFrom: + m.ResetValidFrom() + return nil + case discount.FieldValidUntil: + m.ResetValidUntil() + return nil + case discount.FieldIsActive: + m.ResetIsActive() + return nil + case discount.FieldMaxUses: + m.ResetMaxUses() + return nil + case discount.FieldMinOrderValue: + m.ResetMinOrderValue() + return nil + case discount.FieldIsCombinable: + m.ResetIsCombinable() + return nil + case discount.FieldMetadata: + m.ResetMetadata() + return nil } - if m.FieldCleared(internship.FieldPrice) { - fields = append(fields, internship.FieldPrice) + return fmt.Errorf("unknown Discount field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *DiscountMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *DiscountMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *DiscountMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *DiscountMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *DiscountMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *DiscountMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *DiscountMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Discount unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *DiscountMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Discount edge %s", name) +} + +// FileUploadMutation represents an operation that mutates the FileUpload nodes in the graph. +type FileUploadMutation struct { + config + op Op + typ string + id *string + status *string + created_at *time.Time + updated_at *time.Time + created_by *string + updated_by *string + file_name *string + file_type *string + extension *string + mime_type *string + public_url *string + secure_url *string + provider *string + external_id *string + size_bytes *int64 + addsize_bytes *int64 + file_size *string + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*FileUpload, error) + predicates []predicate.FileUpload +} + +var _ ent.Mutation = (*FileUploadMutation)(nil) + +// fileuploadOption allows management of the mutation configuration using functional options. +type fileuploadOption func(*FileUploadMutation) + +// newFileUploadMutation creates new mutation for the FileUpload entity. +func newFileUploadMutation(c config, op Op, opts ...fileuploadOption) *FileUploadMutation { + m := &FileUploadMutation{ + config: c, + op: op, + typ: TypeFileUpload, + clearedFields: make(map[string]struct{}), } - if m.FieldCleared(internship.FieldFlatDiscount) { - fields = append(fields, internship.FieldFlatDiscount) + for _, opt := range opts { + opt(m) } - if m.FieldCleared(internship.FieldPercentageDiscount) { - fields = append(fields, internship.FieldPercentageDiscount) + return m +} + +// withFileUploadID sets the ID field of the mutation. +func withFileUploadID(id string) fileuploadOption { + return func(m *FileUploadMutation) { + var ( + err error + once sync.Once + value *FileUpload + ) + m.oldValue = func(ctx context.Context) (*FileUpload, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().FileUpload.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withFileUpload sets the old FileUpload of the mutation. +func withFileUpload(node *FileUpload) fileuploadOption { + return func(m *FileUploadMutation) { + m.oldValue = func(context.Context) (*FileUpload, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m FileUploadMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m FileUploadMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of FileUpload entities. +func (m *FileUploadMutation) SetID(id string) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *FileUploadMutation) ID() (id string, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *FileUploadMutation) IDs(ctx context.Context) ([]string, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []string{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().FileUpload.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetStatus sets the "status" field. +func (m *FileUploadMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *FileUploadMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *FileUploadMutation) ResetStatus() { + m.status = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *FileUploadMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *FileUploadMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *FileUploadMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *FileUploadMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *FileUploadMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *FileUploadMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *FileUploadMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *FileUploadMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *FileUploadMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[fileupload.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *FileUploadMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[fileupload.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *FileUploadMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, fileupload.FieldCreatedBy) +} + +// SetUpdatedBy sets the "updated_by" field. +func (m *FileUploadMutation) SetUpdatedBy(s string) { + m.updated_by = &s +} + +// UpdatedBy returns the value of the "updated_by" field in the mutation. +func (m *FileUploadMutation) UpdatedBy() (r string, exists bool) { + v := m.updated_by + if v == nil { + return + } + return *v, true +} + +// OldUpdatedBy returns the old "updated_by" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedBy: %w", err) + } + return oldValue.UpdatedBy, nil +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (m *FileUploadMutation) ClearUpdatedBy() { + m.updated_by = nil + m.clearedFields[fileupload.FieldUpdatedBy] = struct{}{} +} + +// UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. +func (m *FileUploadMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[fileupload.FieldUpdatedBy] + return ok +} + +// ResetUpdatedBy resets all changes to the "updated_by" field. +func (m *FileUploadMutation) ResetUpdatedBy() { + m.updated_by = nil + delete(m.clearedFields, fileupload.FieldUpdatedBy) +} + +// SetFileName sets the "file_name" field. +func (m *FileUploadMutation) SetFileName(s string) { + m.file_name = &s +} + +// FileName returns the value of the "file_name" field in the mutation. +func (m *FileUploadMutation) FileName() (r string, exists bool) { + v := m.file_name + if v == nil { + return + } + return *v, true +} + +// OldFileName returns the old "file_name" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldFileName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFileName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFileName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFileName: %w", err) + } + return oldValue.FileName, nil +} + +// ResetFileName resets all changes to the "file_name" field. +func (m *FileUploadMutation) ResetFileName() { + m.file_name = nil +} + +// SetFileType sets the "file_type" field. +func (m *FileUploadMutation) SetFileType(s string) { + m.file_type = &s +} + +// FileType returns the value of the "file_type" field in the mutation. +func (m *FileUploadMutation) FileType() (r string, exists bool) { + v := m.file_type + if v == nil { + return + } + return *v, true +} + +// OldFileType returns the old "file_type" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldFileType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFileType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFileType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFileType: %w", err) + } + return oldValue.FileType, nil +} + +// ResetFileType resets all changes to the "file_type" field. +func (m *FileUploadMutation) ResetFileType() { + m.file_type = nil +} + +// SetExtension sets the "extension" field. +func (m *FileUploadMutation) SetExtension(s string) { + m.extension = &s +} + +// Extension returns the value of the "extension" field in the mutation. +func (m *FileUploadMutation) Extension() (r string, exists bool) { + v := m.extension + if v == nil { + return + } + return *v, true +} + +// OldExtension returns the old "extension" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldExtension(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExtension is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExtension requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExtension: %w", err) + } + return oldValue.Extension, nil +} + +// ResetExtension resets all changes to the "extension" field. +func (m *FileUploadMutation) ResetExtension() { + m.extension = nil +} + +// SetMimeType sets the "mime_type" field. +func (m *FileUploadMutation) SetMimeType(s string) { + m.mime_type = &s +} + +// MimeType returns the value of the "mime_type" field in the mutation. +func (m *FileUploadMutation) MimeType() (r string, exists bool) { + v := m.mime_type + if v == nil { + return + } + return *v, true +} + +// OldMimeType returns the old "mime_type" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldMimeType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMimeType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMimeType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMimeType: %w", err) + } + return oldValue.MimeType, nil +} + +// ResetMimeType resets all changes to the "mime_type" field. +func (m *FileUploadMutation) ResetMimeType() { + m.mime_type = nil +} + +// SetPublicURL sets the "public_url" field. +func (m *FileUploadMutation) SetPublicURL(s string) { + m.public_url = &s +} + +// PublicURL returns the value of the "public_url" field in the mutation. +func (m *FileUploadMutation) PublicURL() (r string, exists bool) { + v := m.public_url + if v == nil { + return + } + return *v, true +} + +// OldPublicURL returns the old "public_url" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldPublicURL(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPublicURL is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPublicURL requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPublicURL: %w", err) + } + return oldValue.PublicURL, nil +} + +// ResetPublicURL resets all changes to the "public_url" field. +func (m *FileUploadMutation) ResetPublicURL() { + m.public_url = nil +} + +// SetSecureURL sets the "secure_url" field. +func (m *FileUploadMutation) SetSecureURL(s string) { + m.secure_url = &s +} + +// SecureURL returns the value of the "secure_url" field in the mutation. +func (m *FileUploadMutation) SecureURL() (r string, exists bool) { + v := m.secure_url + if v == nil { + return + } + return *v, true +} + +// OldSecureURL returns the old "secure_url" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldSecureURL(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSecureURL is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSecureURL requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSecureURL: %w", err) + } + return oldValue.SecureURL, nil +} + +// ClearSecureURL clears the value of the "secure_url" field. +func (m *FileUploadMutation) ClearSecureURL() { + m.secure_url = nil + m.clearedFields[fileupload.FieldSecureURL] = struct{}{} +} + +// SecureURLCleared returns if the "secure_url" field was cleared in this mutation. +func (m *FileUploadMutation) SecureURLCleared() bool { + _, ok := m.clearedFields[fileupload.FieldSecureURL] + return ok +} + +// ResetSecureURL resets all changes to the "secure_url" field. +func (m *FileUploadMutation) ResetSecureURL() { + m.secure_url = nil + delete(m.clearedFields, fileupload.FieldSecureURL) +} + +// SetProvider sets the "provider" field. +func (m *FileUploadMutation) SetProvider(s string) { + m.provider = &s +} + +// Provider returns the value of the "provider" field in the mutation. +func (m *FileUploadMutation) Provider() (r string, exists bool) { + v := m.provider + if v == nil { + return + } + return *v, true +} + +// OldProvider returns the old "provider" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldProvider(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProvider is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProvider requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProvider: %w", err) + } + return oldValue.Provider, nil +} + +// ResetProvider resets all changes to the "provider" field. +func (m *FileUploadMutation) ResetProvider() { + m.provider = nil +} + +// SetExternalID sets the "external_id" field. +func (m *FileUploadMutation) SetExternalID(s string) { + m.external_id = &s +} + +// ExternalID returns the value of the "external_id" field in the mutation. +func (m *FileUploadMutation) ExternalID() (r string, exists bool) { + v := m.external_id + if v == nil { + return + } + return *v, true +} + +// OldExternalID returns the old "external_id" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldExternalID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExternalID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExternalID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExternalID: %w", err) + } + return oldValue.ExternalID, nil +} + +// ResetExternalID resets all changes to the "external_id" field. +func (m *FileUploadMutation) ResetExternalID() { + m.external_id = nil +} + +// SetSizeBytes sets the "size_bytes" field. +func (m *FileUploadMutation) SetSizeBytes(i int64) { + m.size_bytes = &i + m.addsize_bytes = nil +} + +// SizeBytes returns the value of the "size_bytes" field in the mutation. +func (m *FileUploadMutation) SizeBytes() (r int64, exists bool) { + v := m.size_bytes + if v == nil { + return + } + return *v, true +} + +// OldSizeBytes returns the old "size_bytes" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldSizeBytes(ctx context.Context) (v int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSizeBytes is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSizeBytes requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSizeBytes: %w", err) + } + return oldValue.SizeBytes, nil +} + +// AddSizeBytes adds i to the "size_bytes" field. +func (m *FileUploadMutation) AddSizeBytes(i int64) { + if m.addsize_bytes != nil { + *m.addsize_bytes += i + } else { + m.addsize_bytes = &i + } +} + +// AddedSizeBytes returns the value that was added to the "size_bytes" field in this mutation. +func (m *FileUploadMutation) AddedSizeBytes() (r int64, exists bool) { + v := m.addsize_bytes + if v == nil { + return + } + return *v, true +} + +// ResetSizeBytes resets all changes to the "size_bytes" field. +func (m *FileUploadMutation) ResetSizeBytes() { + m.size_bytes = nil + m.addsize_bytes = nil +} + +// SetFileSize sets the "file_size" field. +func (m *FileUploadMutation) SetFileSize(s string) { + m.file_size = &s +} + +// FileSize returns the value of the "file_size" field in the mutation. +func (m *FileUploadMutation) FileSize() (r string, exists bool) { + v := m.file_size + if v == nil { + return + } + return *v, true +} + +// OldFileSize returns the old "file_size" field's value of the FileUpload entity. +// If the FileUpload object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *FileUploadMutation) OldFileSize(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFileSize is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFileSize requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFileSize: %w", err) + } + return oldValue.FileSize, nil +} + +// ClearFileSize clears the value of the "file_size" field. +func (m *FileUploadMutation) ClearFileSize() { + m.file_size = nil + m.clearedFields[fileupload.FieldFileSize] = struct{}{} +} + +// FileSizeCleared returns if the "file_size" field was cleared in this mutation. +func (m *FileUploadMutation) FileSizeCleared() bool { + _, ok := m.clearedFields[fileupload.FieldFileSize] + return ok +} + +// ResetFileSize resets all changes to the "file_size" field. +func (m *FileUploadMutation) ResetFileSize() { + m.file_size = nil + delete(m.clearedFields, fileupload.FieldFileSize) +} + +// Where appends a list predicates to the FileUploadMutation builder. +func (m *FileUploadMutation) Where(ps ...predicate.FileUpload) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the FileUploadMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *FileUploadMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.FileUpload, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *FileUploadMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *FileUploadMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (FileUpload). +func (m *FileUploadMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *FileUploadMutation) Fields() []string { + fields := make([]string, 0, 15) + if m.status != nil { + fields = append(fields, fileupload.FieldStatus) + } + if m.created_at != nil { + fields = append(fields, fileupload.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, fileupload.FieldUpdatedAt) + } + if m.created_by != nil { + fields = append(fields, fileupload.FieldCreatedBy) + } + if m.updated_by != nil { + fields = append(fields, fileupload.FieldUpdatedBy) + } + if m.file_name != nil { + fields = append(fields, fileupload.FieldFileName) + } + if m.file_type != nil { + fields = append(fields, fileupload.FieldFileType) + } + if m.extension != nil { + fields = append(fields, fileupload.FieldExtension) + } + if m.mime_type != nil { + fields = append(fields, fileupload.FieldMimeType) + } + if m.public_url != nil { + fields = append(fields, fileupload.FieldPublicURL) + } + if m.secure_url != nil { + fields = append(fields, fileupload.FieldSecureURL) + } + if m.provider != nil { + fields = append(fields, fileupload.FieldProvider) + } + if m.external_id != nil { + fields = append(fields, fileupload.FieldExternalID) + } + if m.size_bytes != nil { + fields = append(fields, fileupload.FieldSizeBytes) + } + if m.file_size != nil { + fields = append(fields, fileupload.FieldFileSize) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *FileUploadMutation) Field(name string) (ent.Value, bool) { + switch name { + case fileupload.FieldStatus: + return m.Status() + case fileupload.FieldCreatedAt: + return m.CreatedAt() + case fileupload.FieldUpdatedAt: + return m.UpdatedAt() + case fileupload.FieldCreatedBy: + return m.CreatedBy() + case fileupload.FieldUpdatedBy: + return m.UpdatedBy() + case fileupload.FieldFileName: + return m.FileName() + case fileupload.FieldFileType: + return m.FileType() + case fileupload.FieldExtension: + return m.Extension() + case fileupload.FieldMimeType: + return m.MimeType() + case fileupload.FieldPublicURL: + return m.PublicURL() + case fileupload.FieldSecureURL: + return m.SecureURL() + case fileupload.FieldProvider: + return m.Provider() + case fileupload.FieldExternalID: + return m.ExternalID() + case fileupload.FieldSizeBytes: + return m.SizeBytes() + case fileupload.FieldFileSize: + return m.FileSize() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *FileUploadMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case fileupload.FieldStatus: + return m.OldStatus(ctx) + case fileupload.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case fileupload.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case fileupload.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case fileupload.FieldUpdatedBy: + return m.OldUpdatedBy(ctx) + case fileupload.FieldFileName: + return m.OldFileName(ctx) + case fileupload.FieldFileType: + return m.OldFileType(ctx) + case fileupload.FieldExtension: + return m.OldExtension(ctx) + case fileupload.FieldMimeType: + return m.OldMimeType(ctx) + case fileupload.FieldPublicURL: + return m.OldPublicURL(ctx) + case fileupload.FieldSecureURL: + return m.OldSecureURL(ctx) + case fileupload.FieldProvider: + return m.OldProvider(ctx) + case fileupload.FieldExternalID: + return m.OldExternalID(ctx) + case fileupload.FieldSizeBytes: + return m.OldSizeBytes(ctx) + case fileupload.FieldFileSize: + return m.OldFileSize(ctx) + } + return nil, fmt.Errorf("unknown FileUpload field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *FileUploadMutation) SetField(name string, value ent.Value) error { + switch name { + case fileupload.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case fileupload.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case fileupload.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case fileupload.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case fileupload.FieldUpdatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedBy(v) + return nil + case fileupload.FieldFileName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFileName(v) + return nil + case fileupload.FieldFileType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFileType(v) + return nil + case fileupload.FieldExtension: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExtension(v) + return nil + case fileupload.FieldMimeType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMimeType(v) + return nil + case fileupload.FieldPublicURL: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPublicURL(v) + return nil + case fileupload.FieldSecureURL: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSecureURL(v) + return nil + case fileupload.FieldProvider: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProvider(v) + return nil + case fileupload.FieldExternalID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExternalID(v) + return nil + case fileupload.FieldSizeBytes: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSizeBytes(v) + return nil + case fileupload.FieldFileSize: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFileSize(v) + return nil + } + return fmt.Errorf("unknown FileUpload field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *FileUploadMutation) AddedFields() []string { + var fields []string + if m.addsize_bytes != nil { + fields = append(fields, fileupload.FieldSizeBytes) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *FileUploadMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case fileupload.FieldSizeBytes: + return m.AddedSizeBytes() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *FileUploadMutation) AddField(name string, value ent.Value) error { + switch name { + case fileupload.FieldSizeBytes: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddSizeBytes(v) + return nil + } + return fmt.Errorf("unknown FileUpload numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *FileUploadMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(fileupload.FieldCreatedBy) { + fields = append(fields, fileupload.FieldCreatedBy) + } + if m.FieldCleared(fileupload.FieldUpdatedBy) { + fields = append(fields, fileupload.FieldUpdatedBy) + } + if m.FieldCleared(fileupload.FieldSecureURL) { + fields = append(fields, fileupload.FieldSecureURL) + } + if m.FieldCleared(fileupload.FieldFileSize) { + fields = append(fields, fileupload.FieldFileSize) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *FileUploadMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *FileUploadMutation) ClearField(name string) error { + switch name { + case fileupload.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case fileupload.FieldUpdatedBy: + m.ClearUpdatedBy() + return nil + case fileupload.FieldSecureURL: + m.ClearSecureURL() + return nil + case fileupload.FieldFileSize: + m.ClearFileSize() + return nil + } + return fmt.Errorf("unknown FileUpload nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *FileUploadMutation) ResetField(name string) error { + switch name { + case fileupload.FieldStatus: + m.ResetStatus() + return nil + case fileupload.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case fileupload.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case fileupload.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case fileupload.FieldUpdatedBy: + m.ResetUpdatedBy() + return nil + case fileupload.FieldFileName: + m.ResetFileName() + return nil + case fileupload.FieldFileType: + m.ResetFileType() + return nil + case fileupload.FieldExtension: + m.ResetExtension() + return nil + case fileupload.FieldMimeType: + m.ResetMimeType() + return nil + case fileupload.FieldPublicURL: + m.ResetPublicURL() + return nil + case fileupload.FieldSecureURL: + m.ResetSecureURL() + return nil + case fileupload.FieldProvider: + m.ResetProvider() + return nil + case fileupload.FieldExternalID: + m.ResetExternalID() + return nil + case fileupload.FieldSizeBytes: + m.ResetSizeBytes() + return nil + case fileupload.FieldFileSize: + m.ResetFileSize() + return nil + } + return fmt.Errorf("unknown FileUpload field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *FileUploadMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *FileUploadMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *FileUploadMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *FileUploadMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *FileUploadMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *FileUploadMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *FileUploadMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown FileUpload unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *FileUploadMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown FileUpload edge %s", name) +} + +// InternshipMutation represents an operation that mutates the Internship nodes in the graph. +type InternshipMutation struct { + config + op Op + typ string + id *string + status *string + created_at *time.Time + updated_at *time.Time + created_by *string + updated_by *string + title *string + lookup_key *string + description *string + skills *[]string + appendskills []string + level *string + mode *string + duration_in_weeks *int + addduration_in_weeks *int + learning_outcomes *[]string + appendlearning_outcomes []string + prerequisites *[]string + appendprerequisites []string + benefits *[]string + appendbenefits []string + currency *string + price *decimal.Decimal + flat_discount *decimal.Decimal + percentage_discount *decimal.Decimal + subtotal *decimal.Decimal + total *decimal.Decimal + clearedFields map[string]struct{} + categories map[string]struct{} + removedcategories map[string]struct{} + clearedcategories bool + done bool + oldValue func(context.Context) (*Internship, error) + predicates []predicate.Internship +} + +var _ ent.Mutation = (*InternshipMutation)(nil) + +// internshipOption allows management of the mutation configuration using functional options. +type internshipOption func(*InternshipMutation) + +// newInternshipMutation creates new mutation for the Internship entity. +func newInternshipMutation(c config, op Op, opts ...internshipOption) *InternshipMutation { + m := &InternshipMutation{ + config: c, + op: op, + typ: TypeInternship, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withInternshipID sets the ID field of the mutation. +func withInternshipID(id string) internshipOption { + return func(m *InternshipMutation) { + var ( + err error + once sync.Once + value *Internship + ) + m.oldValue = func(ctx context.Context) (*Internship, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Internship.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withInternship sets the old Internship of the mutation. +func withInternship(node *Internship) internshipOption { + return func(m *InternshipMutation) { + m.oldValue = func(context.Context) (*Internship, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m InternshipMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m InternshipMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Internship entities. +func (m *InternshipMutation) SetID(id string) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *InternshipMutation) ID() (id string, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *InternshipMutation) IDs(ctx context.Context) ([]string, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []string{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Internship.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetStatus sets the "status" field. +func (m *InternshipMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *InternshipMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *InternshipMutation) ResetStatus() { + m.status = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *InternshipMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *InternshipMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *InternshipMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *InternshipMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *InternshipMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *InternshipMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *InternshipMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *InternshipMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *InternshipMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[internship.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *InternshipMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[internship.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *InternshipMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, internship.FieldCreatedBy) +} + +// SetUpdatedBy sets the "updated_by" field. +func (m *InternshipMutation) SetUpdatedBy(s string) { + m.updated_by = &s +} + +// UpdatedBy returns the value of the "updated_by" field in the mutation. +func (m *InternshipMutation) UpdatedBy() (r string, exists bool) { + v := m.updated_by + if v == nil { + return + } + return *v, true +} + +// OldUpdatedBy returns the old "updated_by" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedBy: %w", err) + } + return oldValue.UpdatedBy, nil +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (m *InternshipMutation) ClearUpdatedBy() { + m.updated_by = nil + m.clearedFields[internship.FieldUpdatedBy] = struct{}{} +} + +// UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. +func (m *InternshipMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[internship.FieldUpdatedBy] + return ok +} + +// ResetUpdatedBy resets all changes to the "updated_by" field. +func (m *InternshipMutation) ResetUpdatedBy() { + m.updated_by = nil + delete(m.clearedFields, internship.FieldUpdatedBy) +} + +// SetTitle sets the "title" field. +func (m *InternshipMutation) SetTitle(s string) { + m.title = &s +} + +// Title returns the value of the "title" field in the mutation. +func (m *InternshipMutation) Title() (r string, exists bool) { + v := m.title + if v == nil { + return + } + return *v, true +} + +// OldTitle returns the old "title" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldTitle(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTitle is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTitle requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTitle: %w", err) + } + return oldValue.Title, nil +} + +// ResetTitle resets all changes to the "title" field. +func (m *InternshipMutation) ResetTitle() { + m.title = nil +} + +// SetLookupKey sets the "lookup_key" field. +func (m *InternshipMutation) SetLookupKey(s string) { + m.lookup_key = &s +} + +// LookupKey returns the value of the "lookup_key" field in the mutation. +func (m *InternshipMutation) LookupKey() (r string, exists bool) { + v := m.lookup_key + if v == nil { + return + } + return *v, true +} + +// OldLookupKey returns the old "lookup_key" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldLookupKey(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLookupKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLookupKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLookupKey: %w", err) + } + return oldValue.LookupKey, nil +} + +// ResetLookupKey resets all changes to the "lookup_key" field. +func (m *InternshipMutation) ResetLookupKey() { + m.lookup_key = nil +} + +// SetDescription sets the "description" field. +func (m *InternshipMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *InternshipMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ResetDescription resets all changes to the "description" field. +func (m *InternshipMutation) ResetDescription() { + m.description = nil +} + +// SetSkills sets the "skills" field. +func (m *InternshipMutation) SetSkills(s []string) { + m.skills = &s + m.appendskills = nil +} + +// Skills returns the value of the "skills" field in the mutation. +func (m *InternshipMutation) Skills() (r []string, exists bool) { + v := m.skills + if v == nil { + return + } + return *v, true +} + +// OldSkills returns the old "skills" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldSkills(ctx context.Context) (v []string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSkills is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSkills requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSkills: %w", err) + } + return oldValue.Skills, nil +} + +// AppendSkills adds s to the "skills" field. +func (m *InternshipMutation) AppendSkills(s []string) { + m.appendskills = append(m.appendskills, s...) +} + +// AppendedSkills returns the list of values that were appended to the "skills" field in this mutation. +func (m *InternshipMutation) AppendedSkills() ([]string, bool) { + if len(m.appendskills) == 0 { + return nil, false + } + return m.appendskills, true +} + +// ClearSkills clears the value of the "skills" field. +func (m *InternshipMutation) ClearSkills() { + m.skills = nil + m.appendskills = nil + m.clearedFields[internship.FieldSkills] = struct{}{} +} + +// SkillsCleared returns if the "skills" field was cleared in this mutation. +func (m *InternshipMutation) SkillsCleared() bool { + _, ok := m.clearedFields[internship.FieldSkills] + return ok +} + +// ResetSkills resets all changes to the "skills" field. +func (m *InternshipMutation) ResetSkills() { + m.skills = nil + m.appendskills = nil + delete(m.clearedFields, internship.FieldSkills) +} + +// SetLevel sets the "level" field. +func (m *InternshipMutation) SetLevel(s string) { + m.level = &s +} + +// Level returns the value of the "level" field in the mutation. +func (m *InternshipMutation) Level() (r string, exists bool) { + v := m.level + if v == nil { + return + } + return *v, true +} + +// OldLevel returns the old "level" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldLevel(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLevel is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLevel requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLevel: %w", err) + } + return oldValue.Level, nil +} + +// ClearLevel clears the value of the "level" field. +func (m *InternshipMutation) ClearLevel() { + m.level = nil + m.clearedFields[internship.FieldLevel] = struct{}{} +} + +// LevelCleared returns if the "level" field was cleared in this mutation. +func (m *InternshipMutation) LevelCleared() bool { + _, ok := m.clearedFields[internship.FieldLevel] + return ok +} + +// ResetLevel resets all changes to the "level" field. +func (m *InternshipMutation) ResetLevel() { + m.level = nil + delete(m.clearedFields, internship.FieldLevel) +} + +// SetMode sets the "mode" field. +func (m *InternshipMutation) SetMode(s string) { + m.mode = &s +} + +// Mode returns the value of the "mode" field in the mutation. +func (m *InternshipMutation) Mode() (r string, exists bool) { + v := m.mode + if v == nil { + return + } + return *v, true +} + +// OldMode returns the old "mode" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldMode(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMode is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMode requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMode: %w", err) + } + return oldValue.Mode, nil +} + +// ResetMode resets all changes to the "mode" field. +func (m *InternshipMutation) ResetMode() { + m.mode = nil +} + +// SetDurationInWeeks sets the "duration_in_weeks" field. +func (m *InternshipMutation) SetDurationInWeeks(i int) { + m.duration_in_weeks = &i + m.addduration_in_weeks = nil +} + +// DurationInWeeks returns the value of the "duration_in_weeks" field in the mutation. +func (m *InternshipMutation) DurationInWeeks() (r int, exists bool) { + v := m.duration_in_weeks + if v == nil { + return + } + return *v, true +} + +// OldDurationInWeeks returns the old "duration_in_weeks" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldDurationInWeeks(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDurationInWeeks is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDurationInWeeks requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDurationInWeeks: %w", err) + } + return oldValue.DurationInWeeks, nil +} + +// AddDurationInWeeks adds i to the "duration_in_weeks" field. +func (m *InternshipMutation) AddDurationInWeeks(i int) { + if m.addduration_in_weeks != nil { + *m.addduration_in_weeks += i + } else { + m.addduration_in_weeks = &i + } +} + +// AddedDurationInWeeks returns the value that was added to the "duration_in_weeks" field in this mutation. +func (m *InternshipMutation) AddedDurationInWeeks() (r int, exists bool) { + v := m.addduration_in_weeks + if v == nil { + return + } + return *v, true +} + +// ClearDurationInWeeks clears the value of the "duration_in_weeks" field. +func (m *InternshipMutation) ClearDurationInWeeks() { + m.duration_in_weeks = nil + m.addduration_in_weeks = nil + m.clearedFields[internship.FieldDurationInWeeks] = struct{}{} +} + +// DurationInWeeksCleared returns if the "duration_in_weeks" field was cleared in this mutation. +func (m *InternshipMutation) DurationInWeeksCleared() bool { + _, ok := m.clearedFields[internship.FieldDurationInWeeks] + return ok +} + +// ResetDurationInWeeks resets all changes to the "duration_in_weeks" field. +func (m *InternshipMutation) ResetDurationInWeeks() { + m.duration_in_weeks = nil + m.addduration_in_weeks = nil + delete(m.clearedFields, internship.FieldDurationInWeeks) +} + +// SetLearningOutcomes sets the "learning_outcomes" field. +func (m *InternshipMutation) SetLearningOutcomes(s []string) { + m.learning_outcomes = &s + m.appendlearning_outcomes = nil +} + +// LearningOutcomes returns the value of the "learning_outcomes" field in the mutation. +func (m *InternshipMutation) LearningOutcomes() (r []string, exists bool) { + v := m.learning_outcomes + if v == nil { + return + } + return *v, true +} + +// OldLearningOutcomes returns the old "learning_outcomes" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldLearningOutcomes(ctx context.Context) (v []string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLearningOutcomes is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLearningOutcomes requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLearningOutcomes: %w", err) + } + return oldValue.LearningOutcomes, nil +} + +// AppendLearningOutcomes adds s to the "learning_outcomes" field. +func (m *InternshipMutation) AppendLearningOutcomes(s []string) { + m.appendlearning_outcomes = append(m.appendlearning_outcomes, s...) +} + +// AppendedLearningOutcomes returns the list of values that were appended to the "learning_outcomes" field in this mutation. +func (m *InternshipMutation) AppendedLearningOutcomes() ([]string, bool) { + if len(m.appendlearning_outcomes) == 0 { + return nil, false + } + return m.appendlearning_outcomes, true +} + +// ClearLearningOutcomes clears the value of the "learning_outcomes" field. +func (m *InternshipMutation) ClearLearningOutcomes() { + m.learning_outcomes = nil + m.appendlearning_outcomes = nil + m.clearedFields[internship.FieldLearningOutcomes] = struct{}{} +} + +// LearningOutcomesCleared returns if the "learning_outcomes" field was cleared in this mutation. +func (m *InternshipMutation) LearningOutcomesCleared() bool { + _, ok := m.clearedFields[internship.FieldLearningOutcomes] + return ok +} + +// ResetLearningOutcomes resets all changes to the "learning_outcomes" field. +func (m *InternshipMutation) ResetLearningOutcomes() { + m.learning_outcomes = nil + m.appendlearning_outcomes = nil + delete(m.clearedFields, internship.FieldLearningOutcomes) +} + +// SetPrerequisites sets the "prerequisites" field. +func (m *InternshipMutation) SetPrerequisites(s []string) { + m.prerequisites = &s + m.appendprerequisites = nil +} + +// Prerequisites returns the value of the "prerequisites" field in the mutation. +func (m *InternshipMutation) Prerequisites() (r []string, exists bool) { + v := m.prerequisites + if v == nil { + return + } + return *v, true +} + +// OldPrerequisites returns the old "prerequisites" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldPrerequisites(ctx context.Context) (v []string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrerequisites is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrerequisites requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrerequisites: %w", err) + } + return oldValue.Prerequisites, nil +} + +// AppendPrerequisites adds s to the "prerequisites" field. +func (m *InternshipMutation) AppendPrerequisites(s []string) { + m.appendprerequisites = append(m.appendprerequisites, s...) +} + +// AppendedPrerequisites returns the list of values that were appended to the "prerequisites" field in this mutation. +func (m *InternshipMutation) AppendedPrerequisites() ([]string, bool) { + if len(m.appendprerequisites) == 0 { + return nil, false + } + return m.appendprerequisites, true +} + +// ClearPrerequisites clears the value of the "prerequisites" field. +func (m *InternshipMutation) ClearPrerequisites() { + m.prerequisites = nil + m.appendprerequisites = nil + m.clearedFields[internship.FieldPrerequisites] = struct{}{} +} + +// PrerequisitesCleared returns if the "prerequisites" field was cleared in this mutation. +func (m *InternshipMutation) PrerequisitesCleared() bool { + _, ok := m.clearedFields[internship.FieldPrerequisites] + return ok +} + +// ResetPrerequisites resets all changes to the "prerequisites" field. +func (m *InternshipMutation) ResetPrerequisites() { + m.prerequisites = nil + m.appendprerequisites = nil + delete(m.clearedFields, internship.FieldPrerequisites) +} + +// SetBenefits sets the "benefits" field. +func (m *InternshipMutation) SetBenefits(s []string) { + m.benefits = &s + m.appendbenefits = nil +} + +// Benefits returns the value of the "benefits" field in the mutation. +func (m *InternshipMutation) Benefits() (r []string, exists bool) { + v := m.benefits + if v == nil { + return + } + return *v, true +} + +// OldBenefits returns the old "benefits" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldBenefits(ctx context.Context) (v []string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBenefits is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBenefits requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBenefits: %w", err) + } + return oldValue.Benefits, nil +} + +// AppendBenefits adds s to the "benefits" field. +func (m *InternshipMutation) AppendBenefits(s []string) { + m.appendbenefits = append(m.appendbenefits, s...) +} + +// AppendedBenefits returns the list of values that were appended to the "benefits" field in this mutation. +func (m *InternshipMutation) AppendedBenefits() ([]string, bool) { + if len(m.appendbenefits) == 0 { + return nil, false + } + return m.appendbenefits, true +} + +// ClearBenefits clears the value of the "benefits" field. +func (m *InternshipMutation) ClearBenefits() { + m.benefits = nil + m.appendbenefits = nil + m.clearedFields[internship.FieldBenefits] = struct{}{} +} + +// BenefitsCleared returns if the "benefits" field was cleared in this mutation. +func (m *InternshipMutation) BenefitsCleared() bool { + _, ok := m.clearedFields[internship.FieldBenefits] + return ok +} + +// ResetBenefits resets all changes to the "benefits" field. +func (m *InternshipMutation) ResetBenefits() { + m.benefits = nil + m.appendbenefits = nil + delete(m.clearedFields, internship.FieldBenefits) +} + +// SetCurrency sets the "currency" field. +func (m *InternshipMutation) SetCurrency(s string) { + m.currency = &s +} + +// Currency returns the value of the "currency" field in the mutation. +func (m *InternshipMutation) Currency() (r string, exists bool) { + v := m.currency + if v == nil { + return + } + return *v, true +} + +// OldCurrency returns the old "currency" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldCurrency(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCurrency is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCurrency requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCurrency: %w", err) + } + return oldValue.Currency, nil +} + +// ClearCurrency clears the value of the "currency" field. +func (m *InternshipMutation) ClearCurrency() { + m.currency = nil + m.clearedFields[internship.FieldCurrency] = struct{}{} +} + +// CurrencyCleared returns if the "currency" field was cleared in this mutation. +func (m *InternshipMutation) CurrencyCleared() bool { + _, ok := m.clearedFields[internship.FieldCurrency] + return ok +} + +// ResetCurrency resets all changes to the "currency" field. +func (m *InternshipMutation) ResetCurrency() { + m.currency = nil + delete(m.clearedFields, internship.FieldCurrency) +} + +// SetPrice sets the "price" field. +func (m *InternshipMutation) SetPrice(d decimal.Decimal) { + m.price = &d +} + +// Price returns the value of the "price" field in the mutation. +func (m *InternshipMutation) Price() (r decimal.Decimal, exists bool) { + v := m.price + if v == nil { + return + } + return *v, true +} + +// OldPrice returns the old "price" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldPrice(ctx context.Context) (v decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrice is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrice requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrice: %w", err) + } + return oldValue.Price, nil +} + +// ResetPrice resets all changes to the "price" field. +func (m *InternshipMutation) ResetPrice() { + m.price = nil +} + +// SetFlatDiscount sets the "flat_discount" field. +func (m *InternshipMutation) SetFlatDiscount(d decimal.Decimal) { + m.flat_discount = &d +} + +// FlatDiscount returns the value of the "flat_discount" field in the mutation. +func (m *InternshipMutation) FlatDiscount() (r decimal.Decimal, exists bool) { + v := m.flat_discount + if v == nil { + return + } + return *v, true +} + +// OldFlatDiscount returns the old "flat_discount" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldFlatDiscount(ctx context.Context) (v *decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFlatDiscount is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFlatDiscount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFlatDiscount: %w", err) + } + return oldValue.FlatDiscount, nil +} + +// ClearFlatDiscount clears the value of the "flat_discount" field. +func (m *InternshipMutation) ClearFlatDiscount() { + m.flat_discount = nil + m.clearedFields[internship.FieldFlatDiscount] = struct{}{} +} + +// FlatDiscountCleared returns if the "flat_discount" field was cleared in this mutation. +func (m *InternshipMutation) FlatDiscountCleared() bool { + _, ok := m.clearedFields[internship.FieldFlatDiscount] + return ok +} + +// ResetFlatDiscount resets all changes to the "flat_discount" field. +func (m *InternshipMutation) ResetFlatDiscount() { + m.flat_discount = nil + delete(m.clearedFields, internship.FieldFlatDiscount) +} + +// SetPercentageDiscount sets the "percentage_discount" field. +func (m *InternshipMutation) SetPercentageDiscount(d decimal.Decimal) { + m.percentage_discount = &d +} + +// PercentageDiscount returns the value of the "percentage_discount" field in the mutation. +func (m *InternshipMutation) PercentageDiscount() (r decimal.Decimal, exists bool) { + v := m.percentage_discount + if v == nil { + return + } + return *v, true +} + +// OldPercentageDiscount returns the old "percentage_discount" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldPercentageDiscount(ctx context.Context) (v *decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPercentageDiscount is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPercentageDiscount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPercentageDiscount: %w", err) + } + return oldValue.PercentageDiscount, nil +} + +// ClearPercentageDiscount clears the value of the "percentage_discount" field. +func (m *InternshipMutation) ClearPercentageDiscount() { + m.percentage_discount = nil + m.clearedFields[internship.FieldPercentageDiscount] = struct{}{} +} + +// PercentageDiscountCleared returns if the "percentage_discount" field was cleared in this mutation. +func (m *InternshipMutation) PercentageDiscountCleared() bool { + _, ok := m.clearedFields[internship.FieldPercentageDiscount] + return ok +} + +// ResetPercentageDiscount resets all changes to the "percentage_discount" field. +func (m *InternshipMutation) ResetPercentageDiscount() { + m.percentage_discount = nil + delete(m.clearedFields, internship.FieldPercentageDiscount) +} + +// SetSubtotal sets the "subtotal" field. +func (m *InternshipMutation) SetSubtotal(d decimal.Decimal) { + m.subtotal = &d +} + +// Subtotal returns the value of the "subtotal" field in the mutation. +func (m *InternshipMutation) Subtotal() (r decimal.Decimal, exists bool) { + v := m.subtotal + if v == nil { + return + } + return *v, true +} + +// OldSubtotal returns the old "subtotal" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldSubtotal(ctx context.Context) (v decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSubtotal is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSubtotal requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSubtotal: %w", err) + } + return oldValue.Subtotal, nil +} + +// ResetSubtotal resets all changes to the "subtotal" field. +func (m *InternshipMutation) ResetSubtotal() { + m.subtotal = nil +} + +// SetTotal sets the "total" field. +func (m *InternshipMutation) SetTotal(d decimal.Decimal) { + m.total = &d +} + +// Total returns the value of the "total" field in the mutation. +func (m *InternshipMutation) Total() (r decimal.Decimal, exists bool) { + v := m.total + if v == nil { + return + } + return *v, true +} + +// OldTotal returns the old "total" field's value of the Internship entity. +// If the Internship object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipMutation) OldTotal(ctx context.Context) (v decimal.Decimal, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTotal is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTotal requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTotal: %w", err) + } + return oldValue.Total, nil +} + +// ResetTotal resets all changes to the "total" field. +func (m *InternshipMutation) ResetTotal() { + m.total = nil +} + +// AddCategoryIDs adds the "categories" edge to the Category entity by ids. +func (m *InternshipMutation) AddCategoryIDs(ids ...string) { + if m.categories == nil { + m.categories = make(map[string]struct{}) + } + for i := range ids { + m.categories[ids[i]] = struct{}{} + } +} + +// ClearCategories clears the "categories" edge to the Category entity. +func (m *InternshipMutation) ClearCategories() { + m.clearedcategories = true +} + +// CategoriesCleared reports if the "categories" edge to the Category entity was cleared. +func (m *InternshipMutation) CategoriesCleared() bool { + return m.clearedcategories +} + +// RemoveCategoryIDs removes the "categories" edge to the Category entity by IDs. +func (m *InternshipMutation) RemoveCategoryIDs(ids ...string) { + if m.removedcategories == nil { + m.removedcategories = make(map[string]struct{}) + } + for i := range ids { + delete(m.categories, ids[i]) + m.removedcategories[ids[i]] = struct{}{} + } +} + +// RemovedCategories returns the removed IDs of the "categories" edge to the Category entity. +func (m *InternshipMutation) RemovedCategoriesIDs() (ids []string) { + for id := range m.removedcategories { + ids = append(ids, id) + } + return +} + +// CategoriesIDs returns the "categories" edge IDs in the mutation. +func (m *InternshipMutation) CategoriesIDs() (ids []string) { + for id := range m.categories { + ids = append(ids, id) + } + return +} + +// ResetCategories resets all changes to the "categories" edge. +func (m *InternshipMutation) ResetCategories() { + m.categories = nil + m.clearedcategories = false + m.removedcategories = nil +} + +// Where appends a list predicates to the InternshipMutation builder. +func (m *InternshipMutation) Where(ps ...predicate.Internship) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the InternshipMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *InternshipMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Internship, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *InternshipMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *InternshipMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Internship). +func (m *InternshipMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *InternshipMutation) Fields() []string { + fields := make([]string, 0, 21) + if m.status != nil { + fields = append(fields, internship.FieldStatus) + } + if m.created_at != nil { + fields = append(fields, internship.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, internship.FieldUpdatedAt) + } + if m.created_by != nil { + fields = append(fields, internship.FieldCreatedBy) + } + if m.updated_by != nil { + fields = append(fields, internship.FieldUpdatedBy) + } + if m.title != nil { + fields = append(fields, internship.FieldTitle) + } + if m.lookup_key != nil { + fields = append(fields, internship.FieldLookupKey) + } + if m.description != nil { + fields = append(fields, internship.FieldDescription) + } + if m.skills != nil { + fields = append(fields, internship.FieldSkills) + } + if m.level != nil { + fields = append(fields, internship.FieldLevel) + } + if m.mode != nil { + fields = append(fields, internship.FieldMode) + } + if m.duration_in_weeks != nil { + fields = append(fields, internship.FieldDurationInWeeks) + } + if m.learning_outcomes != nil { + fields = append(fields, internship.FieldLearningOutcomes) + } + if m.prerequisites != nil { + fields = append(fields, internship.FieldPrerequisites) + } + if m.benefits != nil { + fields = append(fields, internship.FieldBenefits) + } + if m.currency != nil { + fields = append(fields, internship.FieldCurrency) + } + if m.price != nil { + fields = append(fields, internship.FieldPrice) + } + if m.flat_discount != nil { + fields = append(fields, internship.FieldFlatDiscount) + } + if m.percentage_discount != nil { + fields = append(fields, internship.FieldPercentageDiscount) + } + if m.subtotal != nil { + fields = append(fields, internship.FieldSubtotal) + } + if m.total != nil { + fields = append(fields, internship.FieldTotal) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *InternshipMutation) Field(name string) (ent.Value, bool) { + switch name { + case internship.FieldStatus: + return m.Status() + case internship.FieldCreatedAt: + return m.CreatedAt() + case internship.FieldUpdatedAt: + return m.UpdatedAt() + case internship.FieldCreatedBy: + return m.CreatedBy() + case internship.FieldUpdatedBy: + return m.UpdatedBy() + case internship.FieldTitle: + return m.Title() + case internship.FieldLookupKey: + return m.LookupKey() + case internship.FieldDescription: + return m.Description() + case internship.FieldSkills: + return m.Skills() + case internship.FieldLevel: + return m.Level() + case internship.FieldMode: + return m.Mode() + case internship.FieldDurationInWeeks: + return m.DurationInWeeks() + case internship.FieldLearningOutcomes: + return m.LearningOutcomes() + case internship.FieldPrerequisites: + return m.Prerequisites() + case internship.FieldBenefits: + return m.Benefits() + case internship.FieldCurrency: + return m.Currency() + case internship.FieldPrice: + return m.Price() + case internship.FieldFlatDiscount: + return m.FlatDiscount() + case internship.FieldPercentageDiscount: + return m.PercentageDiscount() + case internship.FieldSubtotal: + return m.Subtotal() + case internship.FieldTotal: + return m.Total() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *InternshipMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case internship.FieldStatus: + return m.OldStatus(ctx) + case internship.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case internship.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case internship.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case internship.FieldUpdatedBy: + return m.OldUpdatedBy(ctx) + case internship.FieldTitle: + return m.OldTitle(ctx) + case internship.FieldLookupKey: + return m.OldLookupKey(ctx) + case internship.FieldDescription: + return m.OldDescription(ctx) + case internship.FieldSkills: + return m.OldSkills(ctx) + case internship.FieldLevel: + return m.OldLevel(ctx) + case internship.FieldMode: + return m.OldMode(ctx) + case internship.FieldDurationInWeeks: + return m.OldDurationInWeeks(ctx) + case internship.FieldLearningOutcomes: + return m.OldLearningOutcomes(ctx) + case internship.FieldPrerequisites: + return m.OldPrerequisites(ctx) + case internship.FieldBenefits: + return m.OldBenefits(ctx) + case internship.FieldCurrency: + return m.OldCurrency(ctx) + case internship.FieldPrice: + return m.OldPrice(ctx) + case internship.FieldFlatDiscount: + return m.OldFlatDiscount(ctx) + case internship.FieldPercentageDiscount: + return m.OldPercentageDiscount(ctx) + case internship.FieldSubtotal: + return m.OldSubtotal(ctx) + case internship.FieldTotal: + return m.OldTotal(ctx) + } + return nil, fmt.Errorf("unknown Internship field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *InternshipMutation) SetField(name string, value ent.Value) error { + switch name { + case internship.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case internship.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case internship.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case internship.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case internship.FieldUpdatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedBy(v) + return nil + case internship.FieldTitle: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTitle(v) + return nil + case internship.FieldLookupKey: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLookupKey(v) + return nil + case internship.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case internship.FieldSkills: + v, ok := value.([]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSkills(v) + return nil + case internship.FieldLevel: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLevel(v) + return nil + case internship.FieldMode: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMode(v) + return nil + case internship.FieldDurationInWeeks: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDurationInWeeks(v) + return nil + case internship.FieldLearningOutcomes: + v, ok := value.([]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLearningOutcomes(v) + return nil + case internship.FieldPrerequisites: + v, ok := value.([]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPrerequisites(v) + return nil + case internship.FieldBenefits: + v, ok := value.([]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBenefits(v) + return nil + case internship.FieldCurrency: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCurrency(v) + return nil + case internship.FieldPrice: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPrice(v) + return nil + case internship.FieldFlatDiscount: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFlatDiscount(v) + return nil + case internship.FieldPercentageDiscount: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPercentageDiscount(v) + return nil + case internship.FieldSubtotal: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSubtotal(v) + return nil + case internship.FieldTotal: + v, ok := value.(decimal.Decimal) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTotal(v) + return nil + } + return fmt.Errorf("unknown Internship field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *InternshipMutation) AddedFields() []string { + var fields []string + if m.addduration_in_weeks != nil { + fields = append(fields, internship.FieldDurationInWeeks) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *InternshipMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case internship.FieldDurationInWeeks: + return m.AddedDurationInWeeks() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *InternshipMutation) AddField(name string, value ent.Value) error { + switch name { + case internship.FieldDurationInWeeks: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddDurationInWeeks(v) + return nil + } + return fmt.Errorf("unknown Internship numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *InternshipMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(internship.FieldCreatedBy) { + fields = append(fields, internship.FieldCreatedBy) + } + if m.FieldCleared(internship.FieldUpdatedBy) { + fields = append(fields, internship.FieldUpdatedBy) + } + if m.FieldCleared(internship.FieldSkills) { + fields = append(fields, internship.FieldSkills) + } + if m.FieldCleared(internship.FieldLevel) { + fields = append(fields, internship.FieldLevel) + } + if m.FieldCleared(internship.FieldDurationInWeeks) { + fields = append(fields, internship.FieldDurationInWeeks) + } + if m.FieldCleared(internship.FieldLearningOutcomes) { + fields = append(fields, internship.FieldLearningOutcomes) + } + if m.FieldCleared(internship.FieldPrerequisites) { + fields = append(fields, internship.FieldPrerequisites) + } + if m.FieldCleared(internship.FieldBenefits) { + fields = append(fields, internship.FieldBenefits) + } + if m.FieldCleared(internship.FieldCurrency) { + fields = append(fields, internship.FieldCurrency) + } + if m.FieldCleared(internship.FieldFlatDiscount) { + fields = append(fields, internship.FieldFlatDiscount) + } + if m.FieldCleared(internship.FieldPercentageDiscount) { + fields = append(fields, internship.FieldPercentageDiscount) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *InternshipMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *InternshipMutation) ClearField(name string) error { + switch name { + case internship.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case internship.FieldUpdatedBy: + m.ClearUpdatedBy() + return nil + case internship.FieldSkills: + m.ClearSkills() + return nil + case internship.FieldLevel: + m.ClearLevel() + return nil + case internship.FieldDurationInWeeks: + m.ClearDurationInWeeks() + return nil + case internship.FieldLearningOutcomes: + m.ClearLearningOutcomes() + return nil + case internship.FieldPrerequisites: + m.ClearPrerequisites() + return nil + case internship.FieldBenefits: + m.ClearBenefits() + return nil + case internship.FieldCurrency: + m.ClearCurrency() + return nil + case internship.FieldFlatDiscount: + m.ClearFlatDiscount() + return nil + case internship.FieldPercentageDiscount: + m.ClearPercentageDiscount() + return nil + } + return fmt.Errorf("unknown Internship nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *InternshipMutation) ResetField(name string) error { + switch name { + case internship.FieldStatus: + m.ResetStatus() + return nil + case internship.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case internship.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case internship.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case internship.FieldUpdatedBy: + m.ResetUpdatedBy() + return nil + case internship.FieldTitle: + m.ResetTitle() + return nil + case internship.FieldLookupKey: + m.ResetLookupKey() + return nil + case internship.FieldDescription: + m.ResetDescription() + return nil + case internship.FieldSkills: + m.ResetSkills() + return nil + case internship.FieldLevel: + m.ResetLevel() + return nil + case internship.FieldMode: + m.ResetMode() + return nil + case internship.FieldDurationInWeeks: + m.ResetDurationInWeeks() + return nil + case internship.FieldLearningOutcomes: + m.ResetLearningOutcomes() + return nil + case internship.FieldPrerequisites: + m.ResetPrerequisites() + return nil + case internship.FieldBenefits: + m.ResetBenefits() + return nil + case internship.FieldCurrency: + m.ResetCurrency() + return nil + case internship.FieldPrice: + m.ResetPrice() + return nil + case internship.FieldFlatDiscount: + m.ResetFlatDiscount() + return nil + case internship.FieldPercentageDiscount: + m.ResetPercentageDiscount() + return nil + case internship.FieldSubtotal: + m.ResetSubtotal() + return nil + case internship.FieldTotal: + m.ResetTotal() + return nil + } + return fmt.Errorf("unknown Internship field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *InternshipMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.categories != nil { + edges = append(edges, internship.EdgeCategories) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *InternshipMutation) AddedIDs(name string) []ent.Value { + switch name { + case internship.EdgeCategories: + ids := make([]ent.Value, 0, len(m.categories)) + for id := range m.categories { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *InternshipMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + if m.removedcategories != nil { + edges = append(edges, internship.EdgeCategories) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *InternshipMutation) RemovedIDs(name string) []ent.Value { + switch name { + case internship.EdgeCategories: + ids := make([]ent.Value, 0, len(m.removedcategories)) + for id := range m.removedcategories { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *InternshipMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedcategories { + edges = append(edges, internship.EdgeCategories) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *InternshipMutation) EdgeCleared(name string) bool { + switch name { + case internship.EdgeCategories: + return m.clearedcategories + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *InternshipMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown Internship unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *InternshipMutation) ResetEdge(name string) error { + switch name { + case internship.EdgeCategories: + m.ResetCategories() + return nil + } + return fmt.Errorf("unknown Internship edge %s", name) +} + +// InternshipBatchMutation represents an operation that mutates the InternshipBatch nodes in the graph. +type InternshipBatchMutation struct { + config + op Op + typ string + id *string + status *string + created_at *time.Time + updated_at *time.Time + created_by *string + updated_by *string + metadata *map[string]string + internship_id *string + name *string + description *string + start_date *time.Time + end_date *time.Time + batch_status *string + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*InternshipBatch, error) + predicates []predicate.InternshipBatch +} + +var _ ent.Mutation = (*InternshipBatchMutation)(nil) + +// internshipbatchOption allows management of the mutation configuration using functional options. +type internshipbatchOption func(*InternshipBatchMutation) + +// newInternshipBatchMutation creates new mutation for the InternshipBatch entity. +func newInternshipBatchMutation(c config, op Op, opts ...internshipbatchOption) *InternshipBatchMutation { + m := &InternshipBatchMutation{ + config: c, + op: op, + typ: TypeInternshipBatch, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withInternshipBatchID sets the ID field of the mutation. +func withInternshipBatchID(id string) internshipbatchOption { + return func(m *InternshipBatchMutation) { + var ( + err error + once sync.Once + value *InternshipBatch + ) + m.oldValue = func(ctx context.Context) (*InternshipBatch, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().InternshipBatch.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withInternshipBatch sets the old InternshipBatch of the mutation. +func withInternshipBatch(node *InternshipBatch) internshipbatchOption { + return func(m *InternshipBatchMutation) { + m.oldValue = func(context.Context) (*InternshipBatch, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m InternshipBatchMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m InternshipBatchMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of InternshipBatch entities. +func (m *InternshipBatchMutation) SetID(id string) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *InternshipBatchMutation) ID() (id string, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *InternshipBatchMutation) IDs(ctx context.Context) ([]string, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []string{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().InternshipBatch.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetStatus sets the "status" field. +func (m *InternshipBatchMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *InternshipBatchMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *InternshipBatchMutation) ResetStatus() { + m.status = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *InternshipBatchMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *InternshipBatchMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *InternshipBatchMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *InternshipBatchMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *InternshipBatchMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *InternshipBatchMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *InternshipBatchMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *InternshipBatchMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *InternshipBatchMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[internshipbatch.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *InternshipBatchMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[internshipbatch.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *InternshipBatchMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, internshipbatch.FieldCreatedBy) +} + +// SetUpdatedBy sets the "updated_by" field. +func (m *InternshipBatchMutation) SetUpdatedBy(s string) { + m.updated_by = &s +} + +// UpdatedBy returns the value of the "updated_by" field in the mutation. +func (m *InternshipBatchMutation) UpdatedBy() (r string, exists bool) { + v := m.updated_by + if v == nil { + return + } + return *v, true +} + +// OldUpdatedBy returns the old "updated_by" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedBy: %w", err) + } + return oldValue.UpdatedBy, nil +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (m *InternshipBatchMutation) ClearUpdatedBy() { + m.updated_by = nil + m.clearedFields[internshipbatch.FieldUpdatedBy] = struct{}{} +} + +// UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. +func (m *InternshipBatchMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[internshipbatch.FieldUpdatedBy] + return ok +} + +// ResetUpdatedBy resets all changes to the "updated_by" field. +func (m *InternshipBatchMutation) ResetUpdatedBy() { + m.updated_by = nil + delete(m.clearedFields, internshipbatch.FieldUpdatedBy) +} + +// SetMetadata sets the "metadata" field. +func (m *InternshipBatchMutation) SetMetadata(value map[string]string) { + m.metadata = &value +} + +// Metadata returns the value of the "metadata" field in the mutation. +func (m *InternshipBatchMutation) Metadata() (r map[string]string, exists bool) { + v := m.metadata + if v == nil { + return + } + return *v, true +} + +// OldMetadata returns the old "metadata" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldMetadata(ctx context.Context) (v map[string]string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMetadata is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMetadata requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMetadata: %w", err) + } + return oldValue.Metadata, nil +} + +// ClearMetadata clears the value of the "metadata" field. +func (m *InternshipBatchMutation) ClearMetadata() { + m.metadata = nil + m.clearedFields[internshipbatch.FieldMetadata] = struct{}{} +} + +// MetadataCleared returns if the "metadata" field was cleared in this mutation. +func (m *InternshipBatchMutation) MetadataCleared() bool { + _, ok := m.clearedFields[internshipbatch.FieldMetadata] + return ok +} + +// ResetMetadata resets all changes to the "metadata" field. +func (m *InternshipBatchMutation) ResetMetadata() { + m.metadata = nil + delete(m.clearedFields, internshipbatch.FieldMetadata) +} + +// SetInternshipID sets the "internship_id" field. +func (m *InternshipBatchMutation) SetInternshipID(s string) { + m.internship_id = &s +} + +// InternshipID returns the value of the "internship_id" field in the mutation. +func (m *InternshipBatchMutation) InternshipID() (r string, exists bool) { + v := m.internship_id + if v == nil { + return + } + return *v, true +} + +// OldInternshipID returns the old "internship_id" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldInternshipID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldInternshipID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldInternshipID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldInternshipID: %w", err) + } + return oldValue.InternshipID, nil +} + +// ResetInternshipID resets all changes to the "internship_id" field. +func (m *InternshipBatchMutation) ResetInternshipID() { + m.internship_id = nil +} + +// SetName sets the "name" field. +func (m *InternshipBatchMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *InternshipBatchMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *InternshipBatchMutation) ResetName() { + m.name = nil +} + +// SetDescription sets the "description" field. +func (m *InternshipBatchMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *InternshipBatchMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ClearDescription clears the value of the "description" field. +func (m *InternshipBatchMutation) ClearDescription() { + m.description = nil + m.clearedFields[internshipbatch.FieldDescription] = struct{}{} +} + +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *InternshipBatchMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[internshipbatch.FieldDescription] + return ok +} + +// ResetDescription resets all changes to the "description" field. +func (m *InternshipBatchMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, internshipbatch.FieldDescription) +} + +// SetStartDate sets the "start_date" field. +func (m *InternshipBatchMutation) SetStartDate(t time.Time) { + m.start_date = &t +} + +// StartDate returns the value of the "start_date" field in the mutation. +func (m *InternshipBatchMutation) StartDate() (r time.Time, exists bool) { + v := m.start_date + if v == nil { + return + } + return *v, true +} + +// OldStartDate returns the old "start_date" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldStartDate(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStartDate is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStartDate requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStartDate: %w", err) + } + return oldValue.StartDate, nil +} + +// ClearStartDate clears the value of the "start_date" field. +func (m *InternshipBatchMutation) ClearStartDate() { + m.start_date = nil + m.clearedFields[internshipbatch.FieldStartDate] = struct{}{} +} + +// StartDateCleared returns if the "start_date" field was cleared in this mutation. +func (m *InternshipBatchMutation) StartDateCleared() bool { + _, ok := m.clearedFields[internshipbatch.FieldStartDate] + return ok +} + +// ResetStartDate resets all changes to the "start_date" field. +func (m *InternshipBatchMutation) ResetStartDate() { + m.start_date = nil + delete(m.clearedFields, internshipbatch.FieldStartDate) +} + +// SetEndDate sets the "end_date" field. +func (m *InternshipBatchMutation) SetEndDate(t time.Time) { + m.end_date = &t +} + +// EndDate returns the value of the "end_date" field in the mutation. +func (m *InternshipBatchMutation) EndDate() (r time.Time, exists bool) { + v := m.end_date + if v == nil { + return + } + return *v, true +} + +// OldEndDate returns the old "end_date" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldEndDate(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEndDate is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEndDate requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEndDate: %w", err) + } + return oldValue.EndDate, nil +} + +// ClearEndDate clears the value of the "end_date" field. +func (m *InternshipBatchMutation) ClearEndDate() { + m.end_date = nil + m.clearedFields[internshipbatch.FieldEndDate] = struct{}{} +} + +// EndDateCleared returns if the "end_date" field was cleared in this mutation. +func (m *InternshipBatchMutation) EndDateCleared() bool { + _, ok := m.clearedFields[internshipbatch.FieldEndDate] + return ok +} + +// ResetEndDate resets all changes to the "end_date" field. +func (m *InternshipBatchMutation) ResetEndDate() { + m.end_date = nil + delete(m.clearedFields, internshipbatch.FieldEndDate) +} + +// SetBatchStatus sets the "batch_status" field. +func (m *InternshipBatchMutation) SetBatchStatus(s string) { + m.batch_status = &s +} + +// BatchStatus returns the value of the "batch_status" field in the mutation. +func (m *InternshipBatchMutation) BatchStatus() (r string, exists bool) { + v := m.batch_status + if v == nil { + return + } + return *v, true +} + +// OldBatchStatus returns the old "batch_status" field's value of the InternshipBatch entity. +// If the InternshipBatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipBatchMutation) OldBatchStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBatchStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBatchStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBatchStatus: %w", err) + } + return oldValue.BatchStatus, nil +} + +// ResetBatchStatus resets all changes to the "batch_status" field. +func (m *InternshipBatchMutation) ResetBatchStatus() { + m.batch_status = nil +} + +// Where appends a list predicates to the InternshipBatchMutation builder. +func (m *InternshipBatchMutation) Where(ps ...predicate.InternshipBatch) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the InternshipBatchMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *InternshipBatchMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.InternshipBatch, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *InternshipBatchMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *InternshipBatchMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (InternshipBatch). +func (m *InternshipBatchMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *InternshipBatchMutation) Fields() []string { + fields := make([]string, 0, 12) + if m.status != nil { + fields = append(fields, internshipbatch.FieldStatus) + } + if m.created_at != nil { + fields = append(fields, internshipbatch.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, internshipbatch.FieldUpdatedAt) + } + if m.created_by != nil { + fields = append(fields, internshipbatch.FieldCreatedBy) + } + if m.updated_by != nil { + fields = append(fields, internshipbatch.FieldUpdatedBy) + } + if m.metadata != nil { + fields = append(fields, internshipbatch.FieldMetadata) + } + if m.internship_id != nil { + fields = append(fields, internshipbatch.FieldInternshipID) + } + if m.name != nil { + fields = append(fields, internshipbatch.FieldName) + } + if m.description != nil { + fields = append(fields, internshipbatch.FieldDescription) + } + if m.start_date != nil { + fields = append(fields, internshipbatch.FieldStartDate) + } + if m.end_date != nil { + fields = append(fields, internshipbatch.FieldEndDate) + } + if m.batch_status != nil { + fields = append(fields, internshipbatch.FieldBatchStatus) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *InternshipBatchMutation) Field(name string) (ent.Value, bool) { + switch name { + case internshipbatch.FieldStatus: + return m.Status() + case internshipbatch.FieldCreatedAt: + return m.CreatedAt() + case internshipbatch.FieldUpdatedAt: + return m.UpdatedAt() + case internshipbatch.FieldCreatedBy: + return m.CreatedBy() + case internshipbatch.FieldUpdatedBy: + return m.UpdatedBy() + case internshipbatch.FieldMetadata: + return m.Metadata() + case internshipbatch.FieldInternshipID: + return m.InternshipID() + case internshipbatch.FieldName: + return m.Name() + case internshipbatch.FieldDescription: + return m.Description() + case internshipbatch.FieldStartDate: + return m.StartDate() + case internshipbatch.FieldEndDate: + return m.EndDate() + case internshipbatch.FieldBatchStatus: + return m.BatchStatus() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *InternshipBatchMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case internshipbatch.FieldStatus: + return m.OldStatus(ctx) + case internshipbatch.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case internshipbatch.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case internshipbatch.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case internshipbatch.FieldUpdatedBy: + return m.OldUpdatedBy(ctx) + case internshipbatch.FieldMetadata: + return m.OldMetadata(ctx) + case internshipbatch.FieldInternshipID: + return m.OldInternshipID(ctx) + case internshipbatch.FieldName: + return m.OldName(ctx) + case internshipbatch.FieldDescription: + return m.OldDescription(ctx) + case internshipbatch.FieldStartDate: + return m.OldStartDate(ctx) + case internshipbatch.FieldEndDate: + return m.OldEndDate(ctx) + case internshipbatch.FieldBatchStatus: + return m.OldBatchStatus(ctx) + } + return nil, fmt.Errorf("unknown InternshipBatch field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *InternshipBatchMutation) SetField(name string, value ent.Value) error { + switch name { + case internshipbatch.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case internshipbatch.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case internshipbatch.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case internshipbatch.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case internshipbatch.FieldUpdatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedBy(v) + return nil + case internshipbatch.FieldMetadata: + v, ok := value.(map[string]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMetadata(v) + return nil + case internshipbatch.FieldInternshipID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInternshipID(v) + return nil + case internshipbatch.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case internshipbatch.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case internshipbatch.FieldStartDate: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStartDate(v) + return nil + case internshipbatch.FieldEndDate: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEndDate(v) + return nil + case internshipbatch.FieldBatchStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBatchStatus(v) + return nil + } + return fmt.Errorf("unknown InternshipBatch field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *InternshipBatchMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *InternshipBatchMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *InternshipBatchMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown InternshipBatch numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *InternshipBatchMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(internshipbatch.FieldCreatedBy) { + fields = append(fields, internshipbatch.FieldCreatedBy) + } + if m.FieldCleared(internshipbatch.FieldUpdatedBy) { + fields = append(fields, internshipbatch.FieldUpdatedBy) + } + if m.FieldCleared(internshipbatch.FieldMetadata) { + fields = append(fields, internshipbatch.FieldMetadata) + } + if m.FieldCleared(internshipbatch.FieldDescription) { + fields = append(fields, internshipbatch.FieldDescription) + } + if m.FieldCleared(internshipbatch.FieldStartDate) { + fields = append(fields, internshipbatch.FieldStartDate) + } + if m.FieldCleared(internshipbatch.FieldEndDate) { + fields = append(fields, internshipbatch.FieldEndDate) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *InternshipBatchMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *InternshipBatchMutation) ClearField(name string) error { + switch name { + case internshipbatch.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case internshipbatch.FieldUpdatedBy: + m.ClearUpdatedBy() + return nil + case internshipbatch.FieldMetadata: + m.ClearMetadata() + return nil + case internshipbatch.FieldDescription: + m.ClearDescription() + return nil + case internshipbatch.FieldStartDate: + m.ClearStartDate() + return nil + case internshipbatch.FieldEndDate: + m.ClearEndDate() + return nil + } + return fmt.Errorf("unknown InternshipBatch nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *InternshipBatchMutation) ResetField(name string) error { + switch name { + case internshipbatch.FieldStatus: + m.ResetStatus() + return nil + case internshipbatch.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case internshipbatch.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case internshipbatch.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case internshipbatch.FieldUpdatedBy: + m.ResetUpdatedBy() + return nil + case internshipbatch.FieldMetadata: + m.ResetMetadata() + return nil + case internshipbatch.FieldInternshipID: + m.ResetInternshipID() + return nil + case internshipbatch.FieldName: + m.ResetName() + return nil + case internshipbatch.FieldDescription: + m.ResetDescription() + return nil + case internshipbatch.FieldStartDate: + m.ResetStartDate() + return nil + case internshipbatch.FieldEndDate: + m.ResetEndDate() + return nil + case internshipbatch.FieldBatchStatus: + m.ResetBatchStatus() + return nil + } + return fmt.Errorf("unknown InternshipBatch field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *InternshipBatchMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *InternshipBatchMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *InternshipBatchMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *InternshipBatchMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *InternshipBatchMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *InternshipBatchMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *InternshipBatchMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown InternshipBatch unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *InternshipBatchMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown InternshipBatch edge %s", name) +} + +// InternshipEnrollmentMutation represents an operation that mutates the InternshipEnrollment nodes in the graph. +type InternshipEnrollmentMutation struct { + config + op Op + typ string + id *string + status *string + created_at *time.Time + updated_at *time.Time + created_by *string + updated_by *string + metadata *map[string]string + user_id *string + internship_id *string + internship_batch_id *string + enrollment_status *types.InternshipEnrollmentStatus + payment_status *types.PaymentStatus + enrolled_at *time.Time + payment_id *string + refunded_at *time.Time + cancellation_reason *string + refund_reason *string + idempotency_key *string + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*InternshipEnrollment, error) + predicates []predicate.InternshipEnrollment +} + +var _ ent.Mutation = (*InternshipEnrollmentMutation)(nil) + +// internshipenrollmentOption allows management of the mutation configuration using functional options. +type internshipenrollmentOption func(*InternshipEnrollmentMutation) + +// newInternshipEnrollmentMutation creates new mutation for the InternshipEnrollment entity. +func newInternshipEnrollmentMutation(c config, op Op, opts ...internshipenrollmentOption) *InternshipEnrollmentMutation { + m := &InternshipEnrollmentMutation{ + config: c, + op: op, + typ: TypeInternshipEnrollment, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withInternshipEnrollmentID sets the ID field of the mutation. +func withInternshipEnrollmentID(id string) internshipenrollmentOption { + return func(m *InternshipEnrollmentMutation) { + var ( + err error + once sync.Once + value *InternshipEnrollment + ) + m.oldValue = func(ctx context.Context) (*InternshipEnrollment, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().InternshipEnrollment.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withInternshipEnrollment sets the old InternshipEnrollment of the mutation. +func withInternshipEnrollment(node *InternshipEnrollment) internshipenrollmentOption { + return func(m *InternshipEnrollmentMutation) { + m.oldValue = func(context.Context) (*InternshipEnrollment, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m InternshipEnrollmentMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m InternshipEnrollmentMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of InternshipEnrollment entities. +func (m *InternshipEnrollmentMutation) SetID(id string) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *InternshipEnrollmentMutation) ID() (id string, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *InternshipEnrollmentMutation) IDs(ctx context.Context) ([]string, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []string{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().InternshipEnrollment.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetStatus sets the "status" field. +func (m *InternshipEnrollmentMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *InternshipEnrollmentMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *InternshipEnrollmentMutation) ResetStatus() { + m.status = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *InternshipEnrollmentMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *InternshipEnrollmentMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *InternshipEnrollmentMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *InternshipEnrollmentMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *InternshipEnrollmentMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *InternshipEnrollmentMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *InternshipEnrollmentMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *InternshipEnrollmentMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *InternshipEnrollmentMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[internshipenrollment.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *InternshipEnrollmentMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, internshipenrollment.FieldCreatedBy) +} + +// SetUpdatedBy sets the "updated_by" field. +func (m *InternshipEnrollmentMutation) SetUpdatedBy(s string) { + m.updated_by = &s +} + +// UpdatedBy returns the value of the "updated_by" field in the mutation. +func (m *InternshipEnrollmentMutation) UpdatedBy() (r string, exists bool) { + v := m.updated_by + if v == nil { + return + } + return *v, true +} + +// OldUpdatedBy returns the old "updated_by" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedBy: %w", err) + } + return oldValue.UpdatedBy, nil +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (m *InternshipEnrollmentMutation) ClearUpdatedBy() { + m.updated_by = nil + m.clearedFields[internshipenrollment.FieldUpdatedBy] = struct{}{} +} + +// UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldUpdatedBy] + return ok +} + +// ResetUpdatedBy resets all changes to the "updated_by" field. +func (m *InternshipEnrollmentMutation) ResetUpdatedBy() { + m.updated_by = nil + delete(m.clearedFields, internshipenrollment.FieldUpdatedBy) +} + +// SetMetadata sets the "metadata" field. +func (m *InternshipEnrollmentMutation) SetMetadata(value map[string]string) { + m.metadata = &value +} + +// Metadata returns the value of the "metadata" field in the mutation. +func (m *InternshipEnrollmentMutation) Metadata() (r map[string]string, exists bool) { + v := m.metadata + if v == nil { + return + } + return *v, true +} + +// OldMetadata returns the old "metadata" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldMetadata(ctx context.Context) (v map[string]string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMetadata is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMetadata requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMetadata: %w", err) + } + return oldValue.Metadata, nil +} + +// ClearMetadata clears the value of the "metadata" field. +func (m *InternshipEnrollmentMutation) ClearMetadata() { + m.metadata = nil + m.clearedFields[internshipenrollment.FieldMetadata] = struct{}{} +} + +// MetadataCleared returns if the "metadata" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) MetadataCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldMetadata] + return ok +} + +// ResetMetadata resets all changes to the "metadata" field. +func (m *InternshipEnrollmentMutation) ResetMetadata() { + m.metadata = nil + delete(m.clearedFields, internshipenrollment.FieldMetadata) +} + +// SetUserID sets the "user_id" field. +func (m *InternshipEnrollmentMutation) SetUserID(s string) { + m.user_id = &s +} + +// UserID returns the value of the "user_id" field in the mutation. +func (m *InternshipEnrollmentMutation) UserID() (r string, exists bool) { + v := m.user_id + if v == nil { + return + } + return *v, true +} + +// OldUserID returns the old "user_id" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldUserID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUserID: %w", err) + } + return oldValue.UserID, nil +} + +// ResetUserID resets all changes to the "user_id" field. +func (m *InternshipEnrollmentMutation) ResetUserID() { + m.user_id = nil +} + +// SetInternshipID sets the "internship_id" field. +func (m *InternshipEnrollmentMutation) SetInternshipID(s string) { + m.internship_id = &s +} + +// InternshipID returns the value of the "internship_id" field in the mutation. +func (m *InternshipEnrollmentMutation) InternshipID() (r string, exists bool) { + v := m.internship_id + if v == nil { + return + } + return *v, true +} + +// OldInternshipID returns the old "internship_id" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldInternshipID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldInternshipID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldInternshipID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldInternshipID: %w", err) + } + return oldValue.InternshipID, nil +} + +// ResetInternshipID resets all changes to the "internship_id" field. +func (m *InternshipEnrollmentMutation) ResetInternshipID() { + m.internship_id = nil +} + +// SetInternshipBatchID sets the "internship_batch_id" field. +func (m *InternshipEnrollmentMutation) SetInternshipBatchID(s string) { + m.internship_batch_id = &s +} + +// InternshipBatchID returns the value of the "internship_batch_id" field in the mutation. +func (m *InternshipEnrollmentMutation) InternshipBatchID() (r string, exists bool) { + v := m.internship_batch_id + if v == nil { + return + } + return *v, true +} + +// OldInternshipBatchID returns the old "internship_batch_id" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldInternshipBatchID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldInternshipBatchID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldInternshipBatchID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldInternshipBatchID: %w", err) + } + return oldValue.InternshipBatchID, nil +} + +// ResetInternshipBatchID resets all changes to the "internship_batch_id" field. +func (m *InternshipEnrollmentMutation) ResetInternshipBatchID() { + m.internship_batch_id = nil +} + +// SetEnrollmentStatus sets the "enrollment_status" field. +func (m *InternshipEnrollmentMutation) SetEnrollmentStatus(tes types.InternshipEnrollmentStatus) { + m.enrollment_status = &tes +} + +// EnrollmentStatus returns the value of the "enrollment_status" field in the mutation. +func (m *InternshipEnrollmentMutation) EnrollmentStatus() (r types.InternshipEnrollmentStatus, exists bool) { + v := m.enrollment_status + if v == nil { + return + } + return *v, true +} + +// OldEnrollmentStatus returns the old "enrollment_status" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldEnrollmentStatus(ctx context.Context) (v types.InternshipEnrollmentStatus, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEnrollmentStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEnrollmentStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEnrollmentStatus: %w", err) + } + return oldValue.EnrollmentStatus, nil +} + +// ResetEnrollmentStatus resets all changes to the "enrollment_status" field. +func (m *InternshipEnrollmentMutation) ResetEnrollmentStatus() { + m.enrollment_status = nil +} + +// SetPaymentStatus sets the "payment_status" field. +func (m *InternshipEnrollmentMutation) SetPaymentStatus(ts types.PaymentStatus) { + m.payment_status = &ts +} + +// PaymentStatus returns the value of the "payment_status" field in the mutation. +func (m *InternshipEnrollmentMutation) PaymentStatus() (r types.PaymentStatus, exists bool) { + v := m.payment_status + if v == nil { + return + } + return *v, true +} + +// OldPaymentStatus returns the old "payment_status" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldPaymentStatus(ctx context.Context) (v types.PaymentStatus, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPaymentStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPaymentStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPaymentStatus: %w", err) + } + return oldValue.PaymentStatus, nil +} + +// ResetPaymentStatus resets all changes to the "payment_status" field. +func (m *InternshipEnrollmentMutation) ResetPaymentStatus() { + m.payment_status = nil +} + +// SetEnrolledAt sets the "enrolled_at" field. +func (m *InternshipEnrollmentMutation) SetEnrolledAt(t time.Time) { + m.enrolled_at = &t +} + +// EnrolledAt returns the value of the "enrolled_at" field in the mutation. +func (m *InternshipEnrollmentMutation) EnrolledAt() (r time.Time, exists bool) { + v := m.enrolled_at + if v == nil { + return + } + return *v, true +} + +// OldEnrolledAt returns the old "enrolled_at" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldEnrolledAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEnrolledAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEnrolledAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEnrolledAt: %w", err) + } + return oldValue.EnrolledAt, nil +} + +// ClearEnrolledAt clears the value of the "enrolled_at" field. +func (m *InternshipEnrollmentMutation) ClearEnrolledAt() { + m.enrolled_at = nil + m.clearedFields[internshipenrollment.FieldEnrolledAt] = struct{}{} +} + +// EnrolledAtCleared returns if the "enrolled_at" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) EnrolledAtCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldEnrolledAt] + return ok +} + +// ResetEnrolledAt resets all changes to the "enrolled_at" field. +func (m *InternshipEnrollmentMutation) ResetEnrolledAt() { + m.enrolled_at = nil + delete(m.clearedFields, internshipenrollment.FieldEnrolledAt) +} + +// SetPaymentID sets the "payment_id" field. +func (m *InternshipEnrollmentMutation) SetPaymentID(s string) { + m.payment_id = &s +} + +// PaymentID returns the value of the "payment_id" field in the mutation. +func (m *InternshipEnrollmentMutation) PaymentID() (r string, exists bool) { + v := m.payment_id + if v == nil { + return + } + return *v, true +} + +// OldPaymentID returns the old "payment_id" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldPaymentID(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPaymentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPaymentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPaymentID: %w", err) + } + return oldValue.PaymentID, nil +} + +// ClearPaymentID clears the value of the "payment_id" field. +func (m *InternshipEnrollmentMutation) ClearPaymentID() { + m.payment_id = nil + m.clearedFields[internshipenrollment.FieldPaymentID] = struct{}{} +} + +// PaymentIDCleared returns if the "payment_id" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) PaymentIDCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldPaymentID] + return ok +} + +// ResetPaymentID resets all changes to the "payment_id" field. +func (m *InternshipEnrollmentMutation) ResetPaymentID() { + m.payment_id = nil + delete(m.clearedFields, internshipenrollment.FieldPaymentID) +} + +// SetRefundedAt sets the "refunded_at" field. +func (m *InternshipEnrollmentMutation) SetRefundedAt(t time.Time) { + m.refunded_at = &t +} + +// RefundedAt returns the value of the "refunded_at" field in the mutation. +func (m *InternshipEnrollmentMutation) RefundedAt() (r time.Time, exists bool) { + v := m.refunded_at + if v == nil { + return + } + return *v, true +} + +// OldRefundedAt returns the old "refunded_at" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldRefundedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRefundedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRefundedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRefundedAt: %w", err) + } + return oldValue.RefundedAt, nil +} + +// ClearRefundedAt clears the value of the "refunded_at" field. +func (m *InternshipEnrollmentMutation) ClearRefundedAt() { + m.refunded_at = nil + m.clearedFields[internshipenrollment.FieldRefundedAt] = struct{}{} +} + +// RefundedAtCleared returns if the "refunded_at" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) RefundedAtCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldRefundedAt] + return ok +} + +// ResetRefundedAt resets all changes to the "refunded_at" field. +func (m *InternshipEnrollmentMutation) ResetRefundedAt() { + m.refunded_at = nil + delete(m.clearedFields, internshipenrollment.FieldRefundedAt) +} + +// SetCancellationReason sets the "cancellation_reason" field. +func (m *InternshipEnrollmentMutation) SetCancellationReason(s string) { + m.cancellation_reason = &s +} + +// CancellationReason returns the value of the "cancellation_reason" field in the mutation. +func (m *InternshipEnrollmentMutation) CancellationReason() (r string, exists bool) { + v := m.cancellation_reason + if v == nil { + return + } + return *v, true +} + +// OldCancellationReason returns the old "cancellation_reason" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldCancellationReason(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCancellationReason is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCancellationReason requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCancellationReason: %w", err) + } + return oldValue.CancellationReason, nil +} + +// ClearCancellationReason clears the value of the "cancellation_reason" field. +func (m *InternshipEnrollmentMutation) ClearCancellationReason() { + m.cancellation_reason = nil + m.clearedFields[internshipenrollment.FieldCancellationReason] = struct{}{} +} + +// CancellationReasonCleared returns if the "cancellation_reason" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) CancellationReasonCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldCancellationReason] + return ok +} + +// ResetCancellationReason resets all changes to the "cancellation_reason" field. +func (m *InternshipEnrollmentMutation) ResetCancellationReason() { + m.cancellation_reason = nil + delete(m.clearedFields, internshipenrollment.FieldCancellationReason) +} + +// SetRefundReason sets the "refund_reason" field. +func (m *InternshipEnrollmentMutation) SetRefundReason(s string) { + m.refund_reason = &s +} + +// RefundReason returns the value of the "refund_reason" field in the mutation. +func (m *InternshipEnrollmentMutation) RefundReason() (r string, exists bool) { + v := m.refund_reason + if v == nil { + return + } + return *v, true +} + +// OldRefundReason returns the old "refund_reason" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldRefundReason(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRefundReason is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRefundReason requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRefundReason: %w", err) + } + return oldValue.RefundReason, nil +} + +// ClearRefundReason clears the value of the "refund_reason" field. +func (m *InternshipEnrollmentMutation) ClearRefundReason() { + m.refund_reason = nil + m.clearedFields[internshipenrollment.FieldRefundReason] = struct{}{} +} + +// RefundReasonCleared returns if the "refund_reason" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) RefundReasonCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldRefundReason] + return ok +} + +// ResetRefundReason resets all changes to the "refund_reason" field. +func (m *InternshipEnrollmentMutation) ResetRefundReason() { + m.refund_reason = nil + delete(m.clearedFields, internshipenrollment.FieldRefundReason) +} + +// SetIdempotencyKey sets the "idempotency_key" field. +func (m *InternshipEnrollmentMutation) SetIdempotencyKey(s string) { + m.idempotency_key = &s +} + +// IdempotencyKey returns the value of the "idempotency_key" field in the mutation. +func (m *InternshipEnrollmentMutation) IdempotencyKey() (r string, exists bool) { + v := m.idempotency_key + if v == nil { + return + } + return *v, true +} + +// OldIdempotencyKey returns the old "idempotency_key" field's value of the InternshipEnrollment entity. +// If the InternshipEnrollment object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternshipEnrollmentMutation) OldIdempotencyKey(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldIdempotencyKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldIdempotencyKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldIdempotencyKey: %w", err) + } + return oldValue.IdempotencyKey, nil +} + +// ClearIdempotencyKey clears the value of the "idempotency_key" field. +func (m *InternshipEnrollmentMutation) ClearIdempotencyKey() { + m.idempotency_key = nil + m.clearedFields[internshipenrollment.FieldIdempotencyKey] = struct{}{} +} + +// IdempotencyKeyCleared returns if the "idempotency_key" field was cleared in this mutation. +func (m *InternshipEnrollmentMutation) IdempotencyKeyCleared() bool { + _, ok := m.clearedFields[internshipenrollment.FieldIdempotencyKey] + return ok +} + +// ResetIdempotencyKey resets all changes to the "idempotency_key" field. +func (m *InternshipEnrollmentMutation) ResetIdempotencyKey() { + m.idempotency_key = nil + delete(m.clearedFields, internshipenrollment.FieldIdempotencyKey) +} + +// Where appends a list predicates to the InternshipEnrollmentMutation builder. +func (m *InternshipEnrollmentMutation) Where(ps ...predicate.InternshipEnrollment) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the InternshipEnrollmentMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *InternshipEnrollmentMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.InternshipEnrollment, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *InternshipEnrollmentMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *InternshipEnrollmentMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (InternshipEnrollment). +func (m *InternshipEnrollmentMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *InternshipEnrollmentMutation) Fields() []string { + fields := make([]string, 0, 17) + if m.status != nil { + fields = append(fields, internshipenrollment.FieldStatus) + } + if m.created_at != nil { + fields = append(fields, internshipenrollment.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, internshipenrollment.FieldUpdatedAt) + } + if m.created_by != nil { + fields = append(fields, internshipenrollment.FieldCreatedBy) + } + if m.updated_by != nil { + fields = append(fields, internshipenrollment.FieldUpdatedBy) + } + if m.metadata != nil { + fields = append(fields, internshipenrollment.FieldMetadata) + } + if m.user_id != nil { + fields = append(fields, internshipenrollment.FieldUserID) + } + if m.internship_id != nil { + fields = append(fields, internshipenrollment.FieldInternshipID) + } + if m.internship_batch_id != nil { + fields = append(fields, internshipenrollment.FieldInternshipBatchID) + } + if m.enrollment_status != nil { + fields = append(fields, internshipenrollment.FieldEnrollmentStatus) + } + if m.payment_status != nil { + fields = append(fields, internshipenrollment.FieldPaymentStatus) + } + if m.enrolled_at != nil { + fields = append(fields, internshipenrollment.FieldEnrolledAt) + } + if m.payment_id != nil { + fields = append(fields, internshipenrollment.FieldPaymentID) + } + if m.refunded_at != nil { + fields = append(fields, internshipenrollment.FieldRefundedAt) + } + if m.cancellation_reason != nil { + fields = append(fields, internshipenrollment.FieldCancellationReason) + } + if m.refund_reason != nil { + fields = append(fields, internshipenrollment.FieldRefundReason) + } + if m.idempotency_key != nil { + fields = append(fields, internshipenrollment.FieldIdempotencyKey) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *InternshipEnrollmentMutation) Field(name string) (ent.Value, bool) { + switch name { + case internshipenrollment.FieldStatus: + return m.Status() + case internshipenrollment.FieldCreatedAt: + return m.CreatedAt() + case internshipenrollment.FieldUpdatedAt: + return m.UpdatedAt() + case internshipenrollment.FieldCreatedBy: + return m.CreatedBy() + case internshipenrollment.FieldUpdatedBy: + return m.UpdatedBy() + case internshipenrollment.FieldMetadata: + return m.Metadata() + case internshipenrollment.FieldUserID: + return m.UserID() + case internshipenrollment.FieldInternshipID: + return m.InternshipID() + case internshipenrollment.FieldInternshipBatchID: + return m.InternshipBatchID() + case internshipenrollment.FieldEnrollmentStatus: + return m.EnrollmentStatus() + case internshipenrollment.FieldPaymentStatus: + return m.PaymentStatus() + case internshipenrollment.FieldEnrolledAt: + return m.EnrolledAt() + case internshipenrollment.FieldPaymentID: + return m.PaymentID() + case internshipenrollment.FieldRefundedAt: + return m.RefundedAt() + case internshipenrollment.FieldCancellationReason: + return m.CancellationReason() + case internshipenrollment.FieldRefundReason: + return m.RefundReason() + case internshipenrollment.FieldIdempotencyKey: + return m.IdempotencyKey() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *InternshipEnrollmentMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case internshipenrollment.FieldStatus: + return m.OldStatus(ctx) + case internshipenrollment.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case internshipenrollment.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case internshipenrollment.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case internshipenrollment.FieldUpdatedBy: + return m.OldUpdatedBy(ctx) + case internshipenrollment.FieldMetadata: + return m.OldMetadata(ctx) + case internshipenrollment.FieldUserID: + return m.OldUserID(ctx) + case internshipenrollment.FieldInternshipID: + return m.OldInternshipID(ctx) + case internshipenrollment.FieldInternshipBatchID: + return m.OldInternshipBatchID(ctx) + case internshipenrollment.FieldEnrollmentStatus: + return m.OldEnrollmentStatus(ctx) + case internshipenrollment.FieldPaymentStatus: + return m.OldPaymentStatus(ctx) + case internshipenrollment.FieldEnrolledAt: + return m.OldEnrolledAt(ctx) + case internshipenrollment.FieldPaymentID: + return m.OldPaymentID(ctx) + case internshipenrollment.FieldRefundedAt: + return m.OldRefundedAt(ctx) + case internshipenrollment.FieldCancellationReason: + return m.OldCancellationReason(ctx) + case internshipenrollment.FieldRefundReason: + return m.OldRefundReason(ctx) + case internshipenrollment.FieldIdempotencyKey: + return m.OldIdempotencyKey(ctx) + } + return nil, fmt.Errorf("unknown InternshipEnrollment field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *InternshipEnrollmentMutation) SetField(name string, value ent.Value) error { + switch name { + case internshipenrollment.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case internshipenrollment.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case internshipenrollment.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case internshipenrollment.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case internshipenrollment.FieldUpdatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedBy(v) + return nil + case internshipenrollment.FieldMetadata: + v, ok := value.(map[string]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMetadata(v) + return nil + case internshipenrollment.FieldUserID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserID(v) + return nil + case internshipenrollment.FieldInternshipID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInternshipID(v) + return nil + case internshipenrollment.FieldInternshipBatchID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInternshipBatchID(v) + return nil + case internshipenrollment.FieldEnrollmentStatus: + v, ok := value.(types.InternshipEnrollmentStatus) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEnrollmentStatus(v) + return nil + case internshipenrollment.FieldPaymentStatus: + v, ok := value.(types.PaymentStatus) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPaymentStatus(v) + return nil + case internshipenrollment.FieldEnrolledAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEnrolledAt(v) + return nil + case internshipenrollment.FieldPaymentID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPaymentID(v) + return nil + case internshipenrollment.FieldRefundedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRefundedAt(v) + return nil + case internshipenrollment.FieldCancellationReason: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCancellationReason(v) + return nil + case internshipenrollment.FieldRefundReason: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRefundReason(v) + return nil + case internshipenrollment.FieldIdempotencyKey: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetIdempotencyKey(v) + return nil + } + return fmt.Errorf("unknown InternshipEnrollment field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *InternshipEnrollmentMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *InternshipEnrollmentMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *InternshipEnrollmentMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown InternshipEnrollment numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *InternshipEnrollmentMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(internshipenrollment.FieldCreatedBy) { + fields = append(fields, internshipenrollment.FieldCreatedBy) + } + if m.FieldCleared(internshipenrollment.FieldUpdatedBy) { + fields = append(fields, internshipenrollment.FieldUpdatedBy) + } + if m.FieldCleared(internshipenrollment.FieldMetadata) { + fields = append(fields, internshipenrollment.FieldMetadata) + } + if m.FieldCleared(internshipenrollment.FieldEnrolledAt) { + fields = append(fields, internshipenrollment.FieldEnrolledAt) + } + if m.FieldCleared(internshipenrollment.FieldPaymentID) { + fields = append(fields, internshipenrollment.FieldPaymentID) + } + if m.FieldCleared(internshipenrollment.FieldRefundedAt) { + fields = append(fields, internshipenrollment.FieldRefundedAt) + } + if m.FieldCleared(internshipenrollment.FieldCancellationReason) { + fields = append(fields, internshipenrollment.FieldCancellationReason) + } + if m.FieldCleared(internshipenrollment.FieldRefundReason) { + fields = append(fields, internshipenrollment.FieldRefundReason) + } + if m.FieldCleared(internshipenrollment.FieldIdempotencyKey) { + fields = append(fields, internshipenrollment.FieldIdempotencyKey) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *InternshipEnrollmentMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *InternshipEnrollmentMutation) ClearField(name string) error { + switch name { + case internshipenrollment.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case internshipenrollment.FieldUpdatedBy: + m.ClearUpdatedBy() + return nil + case internshipenrollment.FieldMetadata: + m.ClearMetadata() + return nil + case internshipenrollment.FieldEnrolledAt: + m.ClearEnrolledAt() + return nil + case internshipenrollment.FieldPaymentID: + m.ClearPaymentID() + return nil + case internshipenrollment.FieldRefundedAt: + m.ClearRefundedAt() + return nil + case internshipenrollment.FieldCancellationReason: + m.ClearCancellationReason() + return nil + case internshipenrollment.FieldRefundReason: + m.ClearRefundReason() + return nil + case internshipenrollment.FieldIdempotencyKey: + m.ClearIdempotencyKey() + return nil + } + return fmt.Errorf("unknown InternshipEnrollment nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *InternshipEnrollmentMutation) ResetField(name string) error { + switch name { + case internshipenrollment.FieldStatus: + m.ResetStatus() + return nil + case internshipenrollment.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case internshipenrollment.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case internshipenrollment.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case internshipenrollment.FieldUpdatedBy: + m.ResetUpdatedBy() + return nil + case internshipenrollment.FieldMetadata: + m.ResetMetadata() + return nil + case internshipenrollment.FieldUserID: + m.ResetUserID() + return nil + case internshipenrollment.FieldInternshipID: + m.ResetInternshipID() + return nil + case internshipenrollment.FieldInternshipBatchID: + m.ResetInternshipBatchID() + return nil + case internshipenrollment.FieldEnrollmentStatus: + m.ResetEnrollmentStatus() + return nil + case internshipenrollment.FieldPaymentStatus: + m.ResetPaymentStatus() + return nil + case internshipenrollment.FieldEnrolledAt: + m.ResetEnrolledAt() + return nil + case internshipenrollment.FieldPaymentID: + m.ResetPaymentID() + return nil + case internshipenrollment.FieldRefundedAt: + m.ResetRefundedAt() + return nil + case internshipenrollment.FieldCancellationReason: + m.ResetCancellationReason() + return nil + case internshipenrollment.FieldRefundReason: + m.ResetRefundReason() + return nil + case internshipenrollment.FieldIdempotencyKey: + m.ResetIdempotencyKey() + return nil + } + return fmt.Errorf("unknown InternshipEnrollment field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *InternshipEnrollmentMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *InternshipEnrollmentMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *InternshipEnrollmentMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *InternshipEnrollmentMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *InternshipEnrollmentMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *InternshipEnrollmentMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *InternshipEnrollmentMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown InternshipEnrollment unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *InternshipEnrollmentMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown InternshipEnrollment edge %s", name) +} + +// OrderMutation represents an operation that mutates the Order nodes in the graph. +type OrderMutation struct { + config + op Op + typ string + id *int + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Order, error) + predicates []predicate.Order +} + +var _ ent.Mutation = (*OrderMutation)(nil) + +// orderOption allows management of the mutation configuration using functional options. +type orderOption func(*OrderMutation) + +// newOrderMutation creates new mutation for the Order entity. +func newOrderMutation(c config, op Op, opts ...orderOption) *OrderMutation { + m := &OrderMutation{ + config: c, + op: op, + typ: TypeOrder, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withOrderID sets the ID field of the mutation. +func withOrderID(id int) orderOption { + return func(m *OrderMutation) { + var ( + err error + once sync.Once + value *Order + ) + m.oldValue = func(ctx context.Context) (*Order, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Order.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withOrder sets the old Order of the mutation. +func withOrder(node *Order) orderOption { + return func(m *OrderMutation) { + m.oldValue = func(context.Context) (*Order, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m OrderMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m OrderMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *OrderMutation) ID() (id int, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *OrderMutation) IDs(ctx context.Context) ([]int, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Order.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// Where appends a list predicates to the OrderMutation builder. +func (m *OrderMutation) Where(ps ...predicate.Order) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the OrderMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *OrderMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Order, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *OrderMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *OrderMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Order). +func (m *OrderMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *OrderMutation) Fields() []string { + fields := make([]string, 0, 0) + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *OrderMutation) Field(name string) (ent.Value, bool) { + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *OrderMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + return nil, fmt.Errorf("unknown Order field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *OrderMutation) SetField(name string, value ent.Value) error { + switch name { } - return fields + return fmt.Errorf("unknown Order field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *OrderMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *OrderMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *OrderMutation) AddField(name string, value ent.Value) error { + return fmt.Errorf("unknown Order numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *OrderMutation) ClearedFields() []string { + return nil } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *InternshipMutation) FieldCleared(name string) bool { +func (m *OrderMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *InternshipMutation) ClearField(name string) error { - switch name { - case internship.FieldCreatedBy: - m.ClearCreatedBy() - return nil - case internship.FieldUpdatedBy: - m.ClearUpdatedBy() - return nil - case internship.FieldSkills: - m.ClearSkills() - return nil - case internship.FieldLevel: - m.ClearLevel() - return nil - case internship.FieldDurationInWeeks: - m.ClearDurationInWeeks() - return nil - case internship.FieldLearningOutcomes: - m.ClearLearningOutcomes() - return nil - case internship.FieldPrerequisites: - m.ClearPrerequisites() - return nil - case internship.FieldBenefits: - m.ClearBenefits() - return nil - case internship.FieldCurrency: - m.ClearCurrency() - return nil - case internship.FieldPrice: - m.ClearPrice() - return nil - case internship.FieldFlatDiscount: - m.ClearFlatDiscount() - return nil - case internship.FieldPercentageDiscount: - m.ClearPercentageDiscount() - return nil - } - return fmt.Errorf("unknown Internship nullable field %s", name) +func (m *OrderMutation) ClearField(name string) error { + return fmt.Errorf("unknown Order nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *InternshipMutation) ResetField(name string) error { - switch name { - case internship.FieldStatus: - m.ResetStatus() - return nil - case internship.FieldCreatedAt: - m.ResetCreatedAt() - return nil - case internship.FieldUpdatedAt: - m.ResetUpdatedAt() - return nil - case internship.FieldCreatedBy: - m.ResetCreatedBy() - return nil - case internship.FieldUpdatedBy: - m.ResetUpdatedBy() - return nil - case internship.FieldTitle: - m.ResetTitle() - return nil - case internship.FieldLookupKey: - m.ResetLookupKey() - return nil - case internship.FieldDescription: - m.ResetDescription() - return nil - case internship.FieldSkills: - m.ResetSkills() - return nil - case internship.FieldLevel: - m.ResetLevel() - return nil - case internship.FieldMode: - m.ResetMode() - return nil - case internship.FieldDurationInWeeks: - m.ResetDurationInWeeks() - return nil - case internship.FieldLearningOutcomes: - m.ResetLearningOutcomes() - return nil - case internship.FieldPrerequisites: - m.ResetPrerequisites() - return nil - case internship.FieldBenefits: - m.ResetBenefits() - return nil - case internship.FieldCurrency: - m.ResetCurrency() - return nil - case internship.FieldPrice: - m.ResetPrice() - return nil - case internship.FieldFlatDiscount: - m.ResetFlatDiscount() - return nil - case internship.FieldPercentageDiscount: - m.ResetPercentageDiscount() - return nil - } - return fmt.Errorf("unknown Internship field %s", name) +func (m *OrderMutation) ResetField(name string) error { + return fmt.Errorf("unknown Order field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *InternshipMutation) AddedEdges() []string { - edges := make([]string, 0, 1) - if m.categories != nil { - edges = append(edges, internship.EdgeCategories) - } +func (m *OrderMutation) AddedEdges() []string { + edges := make([]string, 0, 0) return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *InternshipMutation) AddedIDs(name string) []ent.Value { - switch name { - case internship.EdgeCategories: - ids := make([]ent.Value, 0, len(m.categories)) - for id := range m.categories { - ids = append(ids, id) - } - return ids - } +func (m *OrderMutation) AddedIDs(name string) []ent.Value { return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *InternshipMutation) RemovedEdges() []string { - edges := make([]string, 0, 1) - if m.removedcategories != nil { - edges = append(edges, internship.EdgeCategories) - } +func (m *OrderMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *InternshipMutation) RemovedIDs(name string) []ent.Value { - switch name { - case internship.EdgeCategories: - ids := make([]ent.Value, 0, len(m.removedcategories)) - for id := range m.removedcategories { - ids = append(ids, id) - } - return ids - } +func (m *OrderMutation) RemovedIDs(name string) []ent.Value { return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *InternshipMutation) ClearedEdges() []string { - edges := make([]string, 0, 1) - if m.clearedcategories { - edges = append(edges, internship.EdgeCategories) - } +func (m *OrderMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *InternshipMutation) EdgeCleared(name string) bool { - switch name { - case internship.EdgeCategories: - return m.clearedcategories - } +func (m *OrderMutation) EdgeCleared(name string) bool { return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *InternshipMutation) ClearEdge(name string) error { - switch name { - } - return fmt.Errorf("unknown Internship unique edge %s", name) +func (m *OrderMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Order unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *InternshipMutation) ResetEdge(name string) error { - switch name { - case internship.EdgeCategories: - m.ResetCategories() - return nil - } - return fmt.Errorf("unknown Internship edge %s", name) +func (m *OrderMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Order edge %s", name) } // PaymentMutation represents an operation that mutates the Payment nodes in the graph. @@ -5626,7 +10891,7 @@ func (m *PaymentMutation) PaymentMethodType() (r types.PaymentMethodType, exists // OldPaymentMethodType returns the old "payment_method_type" field's value of the Payment entity. // If the Payment object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PaymentMutation) OldPaymentMethodType(ctx context.Context) (v types.PaymentMethodType, err error) { +func (m *PaymentMutation) OldPaymentMethodType(ctx context.Context) (v *types.PaymentMethodType, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldPaymentMethodType is only allowed on UpdateOne operations") } @@ -5640,9 +10905,22 @@ func (m *PaymentMutation) OldPaymentMethodType(ctx context.Context) (v types.Pay return oldValue.PaymentMethodType, nil } +// ClearPaymentMethodType clears the value of the "payment_method_type" field. +func (m *PaymentMutation) ClearPaymentMethodType() { + m.payment_method_type = nil + m.clearedFields[payment.FieldPaymentMethodType] = struct{}{} +} + +// PaymentMethodTypeCleared returns if the "payment_method_type" field was cleared in this mutation. +func (m *PaymentMutation) PaymentMethodTypeCleared() bool { + _, ok := m.clearedFields[payment.FieldPaymentMethodType] + return ok +} + // ResetPaymentMethodType resets all changes to the "payment_method_type" field. func (m *PaymentMutation) ResetPaymentMethodType() { m.payment_method_type = nil + delete(m.clearedFields, payment.FieldPaymentMethodType) } // SetPaymentMethodID sets the "payment_method_id" field. @@ -5711,7 +10989,7 @@ func (m *PaymentMutation) PaymentGatewayProvider() (r types.PaymentGatewayProvid // OldPaymentGatewayProvider returns the old "payment_gateway_provider" field's value of the Payment entity. // If the Payment object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PaymentMutation) OldPaymentGatewayProvider(ctx context.Context) (v *types.PaymentGatewayProvider, err error) { +func (m *PaymentMutation) OldPaymentGatewayProvider(ctx context.Context) (v types.PaymentGatewayProvider, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldPaymentGatewayProvider is only allowed on UpdateOne operations") } @@ -5725,22 +11003,9 @@ func (m *PaymentMutation) OldPaymentGatewayProvider(ctx context.Context) (v *typ return oldValue.PaymentGatewayProvider, nil } -// ClearPaymentGatewayProvider clears the value of the "payment_gateway_provider" field. -func (m *PaymentMutation) ClearPaymentGatewayProvider() { - m.payment_gateway_provider = nil - m.clearedFields[payment.FieldPaymentGatewayProvider] = struct{}{} -} - -// PaymentGatewayProviderCleared returns if the "payment_gateway_provider" field was cleared in this mutation. -func (m *PaymentMutation) PaymentGatewayProviderCleared() bool { - _, ok := m.clearedFields[payment.FieldPaymentGatewayProvider] - return ok -} - // ResetPaymentGatewayProvider resets all changes to the "payment_gateway_provider" field. func (m *PaymentMutation) ResetPaymentGatewayProvider() { m.payment_gateway_provider = nil - delete(m.clearedFields, payment.FieldPaymentGatewayProvider) } // SetGatewayPaymentID sets the "gateway_payment_id" field. @@ -6626,12 +11891,12 @@ func (m *PaymentMutation) ClearedFields() []string { if m.FieldCleared(payment.FieldUpdatedBy) { fields = append(fields, payment.FieldUpdatedBy) } + if m.FieldCleared(payment.FieldPaymentMethodType) { + fields = append(fields, payment.FieldPaymentMethodType) + } if m.FieldCleared(payment.FieldPaymentMethodID) { fields = append(fields, payment.FieldPaymentMethodID) } - if m.FieldCleared(payment.FieldPaymentGatewayProvider) { - fields = append(fields, payment.FieldPaymentGatewayProvider) - } if m.FieldCleared(payment.FieldGatewayPaymentID) { fields = append(fields, payment.FieldGatewayPaymentID) } @@ -6670,12 +11935,12 @@ func (m *PaymentMutation) ClearField(name string) error { case payment.FieldUpdatedBy: m.ClearUpdatedBy() return nil + case payment.FieldPaymentMethodType: + m.ClearPaymentMethodType() + return nil case payment.FieldPaymentMethodID: m.ClearPaymentMethodID() return nil - case payment.FieldPaymentGatewayProvider: - m.ClearPaymentGatewayProvider() - return nil case payment.FieldGatewayPaymentID: m.ClearGatewayPaymentID() return nil @@ -7929,6 +13194,9 @@ type UserMutation struct { phone_number *string role *string clearedFields map[string]struct{} + carts map[string]struct{} + removedcarts map[string]struct{} + clearedcarts bool done bool oldValue func(context.Context) (*User, error) predicates []predicate.User @@ -8388,6 +13656,60 @@ func (m *UserMutation) ResetRole() { m.role = nil } +// AddCartIDs adds the "carts" edge to the Cart entity by ids. +func (m *UserMutation) AddCartIDs(ids ...string) { + if m.carts == nil { + m.carts = make(map[string]struct{}) + } + for i := range ids { + m.carts[ids[i]] = struct{}{} + } +} + +// ClearCarts clears the "carts" edge to the Cart entity. +func (m *UserMutation) ClearCarts() { + m.clearedcarts = true +} + +// CartsCleared reports if the "carts" edge to the Cart entity was cleared. +func (m *UserMutation) CartsCleared() bool { + return m.clearedcarts +} + +// RemoveCartIDs removes the "carts" edge to the Cart entity by IDs. +func (m *UserMutation) RemoveCartIDs(ids ...string) { + if m.removedcarts == nil { + m.removedcarts = make(map[string]struct{}) + } + for i := range ids { + delete(m.carts, ids[i]) + m.removedcarts[ids[i]] = struct{}{} + } +} + +// RemovedCarts returns the removed IDs of the "carts" edge to the Cart entity. +func (m *UserMutation) RemovedCartsIDs() (ids []string) { + for id := range m.removedcarts { + ids = append(ids, id) + } + return +} + +// CartsIDs returns the "carts" edge IDs in the mutation. +func (m *UserMutation) CartsIDs() (ids []string) { + for id := range m.carts { + ids = append(ids, id) + } + return +} + +// ResetCarts resets all changes to the "carts" edge. +func (m *UserMutation) ResetCarts() { + m.carts = nil + m.clearedcarts = false + m.removedcarts = nil +} + // Where appends a list predicates to the UserMutation builder. func (m *UserMutation) Where(ps ...predicate.User) { m.predicates = append(m.predicates, ps...) @@ -8672,48 +13994,84 @@ func (m *UserMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *UserMutation) AddedEdges() []string { - edges := make([]string, 0, 0) + edges := make([]string, 0, 1) + if m.carts != nil { + edges = append(edges, user.EdgeCarts) + } return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. func (m *UserMutation) AddedIDs(name string) []ent.Value { + switch name { + case user.EdgeCarts: + ids := make([]ent.Value, 0, len(m.carts)) + for id := range m.carts { + ids = append(ids, id) + } + return ids + } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *UserMutation) RemovedEdges() []string { - edges := make([]string, 0, 0) + edges := make([]string, 0, 1) + if m.removedcarts != nil { + edges = append(edges, user.EdgeCarts) + } return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. func (m *UserMutation) RemovedIDs(name string) []ent.Value { + switch name { + case user.EdgeCarts: + ids := make([]ent.Value, 0, len(m.removedcarts)) + for id := range m.removedcarts { + ids = append(ids, id) + } + return ids + } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 0) + edges := make([]string, 0, 1) + if m.clearedcarts { + edges = append(edges, user.EdgeCarts) + } return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. func (m *UserMutation) EdgeCleared(name string) bool { + switch name { + case user.EdgeCarts: + return m.clearedcarts + } return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. func (m *UserMutation) ClearEdge(name string) error { + switch name { + } return fmt.Errorf("unknown User unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. func (m *UserMutation) ResetEdge(name string) error { + switch name { + case user.EdgeCarts: + m.ResetCarts() + return nil + } return fmt.Errorf("unknown User edge %s", name) } diff --git a/ent/order.go b/ent/order.go new file mode 100644 index 0000000..8736ffc --- /dev/null +++ b/ent/order.go @@ -0,0 +1,91 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/order" +) + +// Order is the model entity for the Order schema. +type Order struct { + config + // ID of the ent. + ID int `json:"id,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Order) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case order.FieldID: + values[i] = new(sql.NullInt64) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Order fields. +func (o *Order) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case order.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + o.ID = int(value.Int64) + default: + o.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Order. +// This includes values selected through modifiers, order, etc. +func (o *Order) Value(name string) (ent.Value, error) { + return o.selectValues.Get(name) +} + +// Update returns a builder for updating this Order. +// Note that you need to call Order.Unwrap() before calling this method if this Order +// was returned from a transaction, and the transaction was committed or rolled back. +func (o *Order) Update() *OrderUpdateOne { + return NewOrderClient(o.config).UpdateOne(o) +} + +// Unwrap unwraps the Order entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (o *Order) Unwrap() *Order { + _tx, ok := o.config.driver.(*txDriver) + if !ok { + panic("ent: Order is not a transactional entity") + } + o.config.driver = _tx.drv + return o +} + +// String implements the fmt.Stringer. +func (o *Order) String() string { + var builder strings.Builder + builder.WriteString("Order(") + builder.WriteString(fmt.Sprintf("id=%v", o.ID)) + builder.WriteByte(')') + return builder.String() +} + +// Orders is a parsable slice of Order. +type Orders []*Order diff --git a/ent/order/order.go b/ent/order/order.go new file mode 100644 index 0000000..72d3c6c --- /dev/null +++ b/ent/order/order.go @@ -0,0 +1,39 @@ +// Code generated by ent, DO NOT EDIT. + +package order + +import ( + "entgo.io/ent/dialect/sql" +) + +const ( + // Label holds the string label denoting the order type in the database. + Label = "order" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // Table holds the table name of the order in the database. + Table = "orders" +) + +// Columns holds all SQL columns for order fields. +var Columns = []string{ + FieldID, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +// OrderOption defines the ordering options for the Order queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} diff --git a/ent/order/where.go b/ent/order/where.go new file mode 100644 index 0000000..5df55d6 --- /dev/null +++ b/ent/order/where.go @@ -0,0 +1,68 @@ +// Code generated by ent, DO NOT EDIT. + +package order + +import ( + "entgo.io/ent/dialect/sql" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.Order { + return predicate.Order(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.Order { + return predicate.Order(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.Order { + return predicate.Order(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.Order { + return predicate.Order(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.Order { + return predicate.Order(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.Order { + return predicate.Order(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.Order { + return predicate.Order(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.Order { + return predicate.Order(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.Order { + return predicate.Order(sql.FieldLTE(FieldID, id)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Order) predicate.Order { + return predicate.Order(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Order) predicate.Order { + return predicate.Order(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Order) predicate.Order { + return predicate.Order(sql.NotPredicates(p)) +} diff --git a/ent/order_create.go b/ent/order_create.go new file mode 100644 index 0000000..d6dac2f --- /dev/null +++ b/ent/order_create.go @@ -0,0 +1,169 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/order" +) + +// OrderCreate is the builder for creating a Order entity. +type OrderCreate struct { + config + mutation *OrderMutation + hooks []Hook +} + +// Mutation returns the OrderMutation object of the builder. +func (oc *OrderCreate) Mutation() *OrderMutation { + return oc.mutation +} + +// Save creates the Order in the database. +func (oc *OrderCreate) Save(ctx context.Context) (*Order, error) { + return withHooks(ctx, oc.sqlSave, oc.mutation, oc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (oc *OrderCreate) SaveX(ctx context.Context) *Order { + v, err := oc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (oc *OrderCreate) Exec(ctx context.Context) error { + _, err := oc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (oc *OrderCreate) ExecX(ctx context.Context) { + if err := oc.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (oc *OrderCreate) check() error { + return nil +} + +func (oc *OrderCreate) sqlSave(ctx context.Context) (*Order, error) { + if err := oc.check(); err != nil { + return nil, err + } + _node, _spec := oc.createSpec() + if err := sqlgraph.CreateNode(ctx, oc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + id := _spec.ID.Value.(int64) + _node.ID = int(id) + oc.mutation.id = &_node.ID + oc.mutation.done = true + return _node, nil +} + +func (oc *OrderCreate) createSpec() (*Order, *sqlgraph.CreateSpec) { + var ( + _node = &Order{config: oc.config} + _spec = sqlgraph.NewCreateSpec(order.Table, sqlgraph.NewFieldSpec(order.FieldID, field.TypeInt)) + ) + return _node, _spec +} + +// OrderCreateBulk is the builder for creating many Order entities in bulk. +type OrderCreateBulk struct { + config + err error + builders []*OrderCreate +} + +// Save creates the Order entities in the database. +func (ocb *OrderCreateBulk) Save(ctx context.Context) ([]*Order, error) { + if ocb.err != nil { + return nil, ocb.err + } + specs := make([]*sqlgraph.CreateSpec, len(ocb.builders)) + nodes := make([]*Order, len(ocb.builders)) + mutators := make([]Mutator, len(ocb.builders)) + for i := range ocb.builders { + func(i int, root context.Context) { + builder := ocb.builders[i] + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*OrderMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, ocb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, ocb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + if specs[i].ID.Value != nil { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int(id) + } + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, ocb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (ocb *OrderCreateBulk) SaveX(ctx context.Context) []*Order { + v, err := ocb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (ocb *OrderCreateBulk) Exec(ctx context.Context) error { + _, err := ocb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ocb *OrderCreateBulk) ExecX(ctx context.Context) { + if err := ocb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/order_delete.go b/ent/order_delete.go new file mode 100644 index 0000000..ea92408 --- /dev/null +++ b/ent/order_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/order" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// OrderDelete is the builder for deleting a Order entity. +type OrderDelete struct { + config + hooks []Hook + mutation *OrderMutation +} + +// Where appends a list predicates to the OrderDelete builder. +func (od *OrderDelete) Where(ps ...predicate.Order) *OrderDelete { + od.mutation.Where(ps...) + return od +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (od *OrderDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, od.sqlExec, od.mutation, od.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (od *OrderDelete) ExecX(ctx context.Context) int { + n, err := od.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (od *OrderDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(order.Table, sqlgraph.NewFieldSpec(order.FieldID, field.TypeInt)) + if ps := od.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, od.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + od.mutation.done = true + return affected, err +} + +// OrderDeleteOne is the builder for deleting a single Order entity. +type OrderDeleteOne struct { + od *OrderDelete +} + +// Where appends a list predicates to the OrderDelete builder. +func (odo *OrderDeleteOne) Where(ps ...predicate.Order) *OrderDeleteOne { + odo.od.mutation.Where(ps...) + return odo +} + +// Exec executes the deletion query. +func (odo *OrderDeleteOne) Exec(ctx context.Context) error { + n, err := odo.od.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{order.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (odo *OrderDeleteOne) ExecX(ctx context.Context) { + if err := odo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/order_query.go b/ent/order_query.go new file mode 100644 index 0000000..37be879 --- /dev/null +++ b/ent/order_query.go @@ -0,0 +1,505 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/order" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// OrderQuery is the builder for querying Order entities. +type OrderQuery struct { + config + ctx *QueryContext + order []order.OrderOption + inters []Interceptor + predicates []predicate.Order + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the OrderQuery builder. +func (oq *OrderQuery) Where(ps ...predicate.Order) *OrderQuery { + oq.predicates = append(oq.predicates, ps...) + return oq +} + +// Limit the number of records to be returned by this query. +func (oq *OrderQuery) Limit(limit int) *OrderQuery { + oq.ctx.Limit = &limit + return oq +} + +// Offset to start from. +func (oq *OrderQuery) Offset(offset int) *OrderQuery { + oq.ctx.Offset = &offset + return oq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (oq *OrderQuery) Unique(unique bool) *OrderQuery { + oq.ctx.Unique = &unique + return oq +} + +// Order specifies how the records should be ordered. +func (oq *OrderQuery) Order(o ...order.OrderOption) *OrderQuery { + oq.order = append(oq.order, o...) + return oq +} + +// First returns the first Order entity from the query. +// Returns a *NotFoundError when no Order was found. +func (oq *OrderQuery) First(ctx context.Context) (*Order, error) { + nodes, err := oq.Limit(1).All(setContextOp(ctx, oq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{order.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (oq *OrderQuery) FirstX(ctx context.Context) *Order { + node, err := oq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Order ID from the query. +// Returns a *NotFoundError when no Order ID was found. +func (oq *OrderQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = oq.Limit(1).IDs(setContextOp(ctx, oq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{order.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (oq *OrderQuery) FirstIDX(ctx context.Context) int { + id, err := oq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Order entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Order entity is found. +// Returns a *NotFoundError when no Order entities are found. +func (oq *OrderQuery) Only(ctx context.Context) (*Order, error) { + nodes, err := oq.Limit(2).All(setContextOp(ctx, oq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{order.Label} + default: + return nil, &NotSingularError{order.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (oq *OrderQuery) OnlyX(ctx context.Context) *Order { + node, err := oq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Order ID in the query. +// Returns a *NotSingularError when more than one Order ID is found. +// Returns a *NotFoundError when no entities are found. +func (oq *OrderQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = oq.Limit(2).IDs(setContextOp(ctx, oq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{order.Label} + default: + err = &NotSingularError{order.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (oq *OrderQuery) OnlyIDX(ctx context.Context) int { + id, err := oq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Orders. +func (oq *OrderQuery) All(ctx context.Context) ([]*Order, error) { + ctx = setContextOp(ctx, oq.ctx, ent.OpQueryAll) + if err := oq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Order, *OrderQuery]() + return withInterceptors[[]*Order](ctx, oq, qr, oq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (oq *OrderQuery) AllX(ctx context.Context) []*Order { + nodes, err := oq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Order IDs. +func (oq *OrderQuery) IDs(ctx context.Context) (ids []int, err error) { + if oq.ctx.Unique == nil && oq.path != nil { + oq.Unique(true) + } + ctx = setContextOp(ctx, oq.ctx, ent.OpQueryIDs) + if err = oq.Select(order.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (oq *OrderQuery) IDsX(ctx context.Context) []int { + ids, err := oq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (oq *OrderQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, oq.ctx, ent.OpQueryCount) + if err := oq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, oq, querierCount[*OrderQuery](), oq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (oq *OrderQuery) CountX(ctx context.Context) int { + count, err := oq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (oq *OrderQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, oq.ctx, ent.OpQueryExist) + switch _, err := oq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (oq *OrderQuery) ExistX(ctx context.Context) bool { + exist, err := oq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the OrderQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (oq *OrderQuery) Clone() *OrderQuery { + if oq == nil { + return nil + } + return &OrderQuery{ + config: oq.config, + ctx: oq.ctx.Clone(), + order: append([]order.OrderOption{}, oq.order...), + inters: append([]Interceptor{}, oq.inters...), + predicates: append([]predicate.Order{}, oq.predicates...), + // clone intermediate query. + sql: oq.sql.Clone(), + path: oq.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +func (oq *OrderQuery) GroupBy(field string, fields ...string) *OrderGroupBy { + oq.ctx.Fields = append([]string{field}, fields...) + grbuild := &OrderGroupBy{build: oq} + grbuild.flds = &oq.ctx.Fields + grbuild.label = order.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +func (oq *OrderQuery) Select(fields ...string) *OrderSelect { + oq.ctx.Fields = append(oq.ctx.Fields, fields...) + sbuild := &OrderSelect{OrderQuery: oq} + sbuild.label = order.Label + sbuild.flds, sbuild.scan = &oq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a OrderSelect configured with the given aggregations. +func (oq *OrderQuery) Aggregate(fns ...AggregateFunc) *OrderSelect { + return oq.Select().Aggregate(fns...) +} + +func (oq *OrderQuery) prepareQuery(ctx context.Context) error { + for _, inter := range oq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, oq); err != nil { + return err + } + } + } + for _, f := range oq.ctx.Fields { + if !order.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if oq.path != nil { + prev, err := oq.path(ctx) + if err != nil { + return err + } + oq.sql = prev + } + return nil +} + +func (oq *OrderQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Order, error) { + var ( + nodes = []*Order{} + _spec = oq.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Order).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Order{config: oq.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, oq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (oq *OrderQuery) sqlCount(ctx context.Context) (int, error) { + _spec := oq.querySpec() + _spec.Node.Columns = oq.ctx.Fields + if len(oq.ctx.Fields) > 0 { + _spec.Unique = oq.ctx.Unique != nil && *oq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, oq.driver, _spec) +} + +func (oq *OrderQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(order.Table, order.Columns, sqlgraph.NewFieldSpec(order.FieldID, field.TypeInt)) + _spec.From = oq.sql + if unique := oq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if oq.path != nil { + _spec.Unique = true + } + if fields := oq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, order.FieldID) + for i := range fields { + if fields[i] != order.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := oq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := oq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := oq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := oq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (oq *OrderQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(oq.driver.Dialect()) + t1 := builder.Table(order.Table) + columns := oq.ctx.Fields + if len(columns) == 0 { + columns = order.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if oq.sql != nil { + selector = oq.sql + selector.Select(selector.Columns(columns...)...) + } + if oq.ctx.Unique != nil && *oq.ctx.Unique { + selector.Distinct() + } + for _, p := range oq.predicates { + p(selector) + } + for _, p := range oq.order { + p(selector) + } + if offset := oq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := oq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// OrderGroupBy is the group-by builder for Order entities. +type OrderGroupBy struct { + selector + build *OrderQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (ogb *OrderGroupBy) Aggregate(fns ...AggregateFunc) *OrderGroupBy { + ogb.fns = append(ogb.fns, fns...) + return ogb +} + +// Scan applies the selector query and scans the result into the given value. +func (ogb *OrderGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, ogb.build.ctx, ent.OpQueryGroupBy) + if err := ogb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*OrderQuery, *OrderGroupBy](ctx, ogb.build, ogb, ogb.build.inters, v) +} + +func (ogb *OrderGroupBy) sqlScan(ctx context.Context, root *OrderQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(ogb.fns)) + for _, fn := range ogb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*ogb.flds)+len(ogb.fns)) + for _, f := range *ogb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*ogb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := ogb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// OrderSelect is the builder for selecting fields of Order entities. +type OrderSelect struct { + *OrderQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (os *OrderSelect) Aggregate(fns ...AggregateFunc) *OrderSelect { + os.fns = append(os.fns, fns...) + return os +} + +// Scan applies the selector query and scans the result into the given value. +func (os *OrderSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, os.ctx, ent.OpQuerySelect) + if err := os.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*OrderQuery, *OrderSelect](ctx, os.OrderQuery, os, os.inters, v) +} + +func (os *OrderSelect) sqlScan(ctx context.Context, root *OrderQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(os.fns)) + for _, fn := range os.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*os.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := os.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/order_update.go b/ent/order_update.go new file mode 100644 index 0000000..5ef1be8 --- /dev/null +++ b/ent/order_update.go @@ -0,0 +1,175 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/order" + "github.com/omkar273/codegeeky/ent/predicate" +) + +// OrderUpdate is the builder for updating Order entities. +type OrderUpdate struct { + config + hooks []Hook + mutation *OrderMutation +} + +// Where appends a list predicates to the OrderUpdate builder. +func (ou *OrderUpdate) Where(ps ...predicate.Order) *OrderUpdate { + ou.mutation.Where(ps...) + return ou +} + +// Mutation returns the OrderMutation object of the builder. +func (ou *OrderUpdate) Mutation() *OrderMutation { + return ou.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (ou *OrderUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, ou.sqlSave, ou.mutation, ou.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ou *OrderUpdate) SaveX(ctx context.Context) int { + affected, err := ou.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (ou *OrderUpdate) Exec(ctx context.Context) error { + _, err := ou.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ou *OrderUpdate) ExecX(ctx context.Context) { + if err := ou.Exec(ctx); err != nil { + panic(err) + } +} + +func (ou *OrderUpdate) sqlSave(ctx context.Context) (n int, err error) { + _spec := sqlgraph.NewUpdateSpec(order.Table, order.Columns, sqlgraph.NewFieldSpec(order.FieldID, field.TypeInt)) + if ps := ou.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if n, err = sqlgraph.UpdateNodes(ctx, ou.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{order.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + ou.mutation.done = true + return n, nil +} + +// OrderUpdateOne is the builder for updating a single Order entity. +type OrderUpdateOne struct { + config + fields []string + hooks []Hook + mutation *OrderMutation +} + +// Mutation returns the OrderMutation object of the builder. +func (ouo *OrderUpdateOne) Mutation() *OrderMutation { + return ouo.mutation +} + +// Where appends a list predicates to the OrderUpdate builder. +func (ouo *OrderUpdateOne) Where(ps ...predicate.Order) *OrderUpdateOne { + ouo.mutation.Where(ps...) + return ouo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (ouo *OrderUpdateOne) Select(field string, fields ...string) *OrderUpdateOne { + ouo.fields = append([]string{field}, fields...) + return ouo +} + +// Save executes the query and returns the updated Order entity. +func (ouo *OrderUpdateOne) Save(ctx context.Context) (*Order, error) { + return withHooks(ctx, ouo.sqlSave, ouo.mutation, ouo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ouo *OrderUpdateOne) SaveX(ctx context.Context) *Order { + node, err := ouo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (ouo *OrderUpdateOne) Exec(ctx context.Context) error { + _, err := ouo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ouo *OrderUpdateOne) ExecX(ctx context.Context) { + if err := ouo.Exec(ctx); err != nil { + panic(err) + } +} + +func (ouo *OrderUpdateOne) sqlSave(ctx context.Context) (_node *Order, err error) { + _spec := sqlgraph.NewUpdateSpec(order.Table, order.Columns, sqlgraph.NewFieldSpec(order.FieldID, field.TypeInt)) + id, ok := ouo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Order.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := ouo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, order.FieldID) + for _, f := range fields { + if !order.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != order.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := ouo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + _node = &Order{config: ouo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, ouo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{order.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + ouo.mutation.done = true + return _node, nil +} diff --git a/ent/payment.go b/ent/payment.go index b27ee20..84b880b 100644 --- a/ent/payment.go +++ b/ent/payment.go @@ -37,11 +37,11 @@ type Payment struct { // DestinationID holds the value of the "destination_id" field. DestinationID string `json:"destination_id,omitempty"` // PaymentMethodType holds the value of the "payment_method_type" field. - PaymentMethodType types.PaymentMethodType `json:"payment_method_type,omitempty"` + PaymentMethodType *types.PaymentMethodType `json:"payment_method_type,omitempty"` // PaymentMethodID holds the value of the "payment_method_id" field. PaymentMethodID string `json:"payment_method_id,omitempty"` // PaymentGatewayProvider holds the value of the "payment_gateway_provider" field. - PaymentGatewayProvider *types.PaymentGatewayProvider `json:"payment_gateway_provider,omitempty"` + PaymentGatewayProvider types.PaymentGatewayProvider `json:"payment_gateway_provider,omitempty"` // GatewayPaymentID holds the value of the "gateway_payment_id" field. GatewayPaymentID *string `json:"gateway_payment_id,omitempty"` // Amount holds the value of the "amount" field. @@ -174,7 +174,8 @@ func (pa *Payment) assignValues(columns []string, values []any) error { if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field payment_method_type", values[i]) } else if value.Valid { - pa.PaymentMethodType = types.PaymentMethodType(value.String) + pa.PaymentMethodType = new(types.PaymentMethodType) + *pa.PaymentMethodType = types.PaymentMethodType(value.String) } case payment.FieldPaymentMethodID: if value, ok := values[i].(*sql.NullString); !ok { @@ -186,8 +187,7 @@ func (pa *Payment) assignValues(columns []string, values []any) error { if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field payment_gateway_provider", values[i]) } else if value.Valid { - pa.PaymentGatewayProvider = new(types.PaymentGatewayProvider) - *pa.PaymentGatewayProvider = types.PaymentGatewayProvider(value.String) + pa.PaymentGatewayProvider = types.PaymentGatewayProvider(value.String) } case payment.FieldGatewayPaymentID: if value, ok := values[i].(*sql.NullString); !ok { @@ -321,16 +321,16 @@ func (pa *Payment) String() string { builder.WriteString("destination_id=") builder.WriteString(pa.DestinationID) builder.WriteString(", ") - builder.WriteString("payment_method_type=") - builder.WriteString(fmt.Sprintf("%v", pa.PaymentMethodType)) + if v := pa.PaymentMethodType; v != nil { + builder.WriteString("payment_method_type=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } builder.WriteString(", ") builder.WriteString("payment_method_id=") builder.WriteString(pa.PaymentMethodID) builder.WriteString(", ") - if v := pa.PaymentGatewayProvider; v != nil { - builder.WriteString("payment_gateway_provider=") - builder.WriteString(fmt.Sprintf("%v", *v)) - } + builder.WriteString("payment_gateway_provider=") + builder.WriteString(fmt.Sprintf("%v", pa.PaymentGatewayProvider)) builder.WriteString(", ") if v := pa.GatewayPaymentID; v != nil { builder.WriteString("gateway_payment_id=") diff --git a/ent/payment/payment.go b/ent/payment/payment.go index db1a661..9e54247 100644 --- a/ent/payment/payment.go +++ b/ent/payment/payment.go @@ -7,6 +7,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/omkar273/codegeeky/internal/types" "github.com/shopspring/decimal" ) @@ -115,20 +116,18 @@ var ( DefaultUpdatedAt func() time.Time // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. UpdateDefaultUpdatedAt func() time.Time - // DestinationTypeValidator is a validator for the "destination_type" field. It is called by the builders before save. - DestinationTypeValidator func(string) error - // DestinationIDValidator is a validator for the "destination_id" field. It is called by the builders before save. - DestinationIDValidator func(string) error - // PaymentMethodTypeValidator is a validator for the "payment_method_type" field. It is called by the builders before save. - PaymentMethodTypeValidator func(string) error // DefaultAmount holds the default value on creation for the "amount" field. DefaultAmount decimal.Decimal // CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. CurrencyValidator func(string) error + // DefaultPaymentStatus holds the default value on creation for the "payment_status" field. + DefaultPaymentStatus types.PaymentStatus // PaymentStatusValidator is a validator for the "payment_status" field. It is called by the builders before save. PaymentStatusValidator func(string) error // DefaultTrackAttempts holds the default value on creation for the "track_attempts" field. DefaultTrackAttempts bool + // DefaultMetadata holds the default value on creation for the "metadata" field. + DefaultMetadata map[string]string ) // OrderOption defines the ordering options for the Payment queries. diff --git a/ent/payment/where.go b/ent/payment/where.go index b125a64..c29e392 100644 --- a/ent/payment/where.go +++ b/ent/payment/where.go @@ -753,6 +753,16 @@ func PaymentMethodTypeHasSuffix(v types.PaymentMethodType) predicate.Payment { return predicate.Payment(sql.FieldHasSuffix(FieldPaymentMethodType, vc)) } +// PaymentMethodTypeIsNil applies the IsNil predicate on the "payment_method_type" field. +func PaymentMethodTypeIsNil() predicate.Payment { + return predicate.Payment(sql.FieldIsNull(FieldPaymentMethodType)) +} + +// PaymentMethodTypeNotNil applies the NotNil predicate on the "payment_method_type" field. +func PaymentMethodTypeNotNil() predicate.Payment { + return predicate.Payment(sql.FieldNotNull(FieldPaymentMethodType)) +} + // PaymentMethodTypeEqualFold applies the EqualFold predicate on the "payment_method_type" field. func PaymentMethodTypeEqualFold(v types.PaymentMethodType) predicate.Payment { vc := string(v) @@ -912,16 +922,6 @@ func PaymentGatewayProviderHasSuffix(v types.PaymentGatewayProvider) predicate.P return predicate.Payment(sql.FieldHasSuffix(FieldPaymentGatewayProvider, vc)) } -// PaymentGatewayProviderIsNil applies the IsNil predicate on the "payment_gateway_provider" field. -func PaymentGatewayProviderIsNil() predicate.Payment { - return predicate.Payment(sql.FieldIsNull(FieldPaymentGatewayProvider)) -} - -// PaymentGatewayProviderNotNil applies the NotNil predicate on the "payment_gateway_provider" field. -func PaymentGatewayProviderNotNil() predicate.Payment { - return predicate.Payment(sql.FieldNotNull(FieldPaymentGatewayProvider)) -} - // PaymentGatewayProviderEqualFold applies the EqualFold predicate on the "payment_gateway_provider" field. func PaymentGatewayProviderEqualFold(v types.PaymentGatewayProvider) predicate.Payment { vc := string(v) diff --git a/ent/payment_create.go b/ent/payment_create.go index a0730d8..4a99517 100644 --- a/ent/payment_create.go +++ b/ent/payment_create.go @@ -117,6 +117,14 @@ func (pc *PaymentCreate) SetPaymentMethodType(tmt types.PaymentMethodType) *Paym return pc } +// SetNillablePaymentMethodType sets the "payment_method_type" field if the given value is not nil. +func (pc *PaymentCreate) SetNillablePaymentMethodType(tmt *types.PaymentMethodType) *PaymentCreate { + if tmt != nil { + pc.SetPaymentMethodType(*tmt) + } + return pc +} + // SetPaymentMethodID sets the "payment_method_id" field. func (pc *PaymentCreate) SetPaymentMethodID(s string) *PaymentCreate { pc.mutation.SetPaymentMethodID(s) @@ -137,14 +145,6 @@ func (pc *PaymentCreate) SetPaymentGatewayProvider(tgp types.PaymentGatewayProvi return pc } -// SetNillablePaymentGatewayProvider sets the "payment_gateway_provider" field if the given value is not nil. -func (pc *PaymentCreate) SetNillablePaymentGatewayProvider(tgp *types.PaymentGatewayProvider) *PaymentCreate { - if tgp != nil { - pc.SetPaymentGatewayProvider(*tgp) - } - return pc -} - // SetGatewayPaymentID sets the "gateway_payment_id" field. func (pc *PaymentCreate) SetGatewayPaymentID(s string) *PaymentCreate { pc.mutation.SetGatewayPaymentID(s) @@ -185,6 +185,14 @@ func (pc *PaymentCreate) SetPaymentStatus(ts types.PaymentStatus) *PaymentCreate return pc } +// SetNillablePaymentStatus sets the "payment_status" field if the given value is not nil. +func (pc *PaymentCreate) SetNillablePaymentStatus(ts *types.PaymentStatus) *PaymentCreate { + if ts != nil { + pc.SetPaymentStatus(*ts) + } + return pc +} + // SetTrackAttempts sets the "track_attempts" field. func (pc *PaymentCreate) SetTrackAttempts(b bool) *PaymentCreate { pc.mutation.SetTrackAttempts(b) @@ -333,10 +341,18 @@ func (pc *PaymentCreate) defaults() { v := payment.DefaultAmount pc.mutation.SetAmount(v) } + if _, ok := pc.mutation.PaymentStatus(); !ok { + v := payment.DefaultPaymentStatus + pc.mutation.SetPaymentStatus(v) + } if _, ok := pc.mutation.TrackAttempts(); !ok { v := payment.DefaultTrackAttempts pc.mutation.SetTrackAttempts(v) } + if _, ok := pc.mutation.Metadata(); !ok { + v := payment.DefaultMetadata + pc.mutation.SetMetadata(v) + } } // check runs all checks and user-defined validators on the builder. @@ -357,26 +373,21 @@ func (pc *PaymentCreate) check() error { return &ValidationError{Name: "destination_type", err: errors.New(`ent: missing required field "Payment.destination_type"`)} } if v, ok := pc.mutation.DestinationType(); ok { - if err := payment.DestinationTypeValidator(string(v)); err != nil { + if err := v.Validate(); err != nil { return &ValidationError{Name: "destination_type", err: fmt.Errorf(`ent: validator failed for field "Payment.destination_type": %w`, err)} } } if _, ok := pc.mutation.DestinationID(); !ok { return &ValidationError{Name: "destination_id", err: errors.New(`ent: missing required field "Payment.destination_id"`)} } - if v, ok := pc.mutation.DestinationID(); ok { - if err := payment.DestinationIDValidator(v); err != nil { - return &ValidationError{Name: "destination_id", err: fmt.Errorf(`ent: validator failed for field "Payment.destination_id": %w`, err)} - } - } - if _, ok := pc.mutation.PaymentMethodType(); !ok { - return &ValidationError{Name: "payment_method_type", err: errors.New(`ent: missing required field "Payment.payment_method_type"`)} - } if v, ok := pc.mutation.PaymentMethodType(); ok { - if err := payment.PaymentMethodTypeValidator(string(v)); err != nil { + if err := v.Validate(); err != nil { return &ValidationError{Name: "payment_method_type", err: fmt.Errorf(`ent: validator failed for field "Payment.payment_method_type": %w`, err)} } } + if _, ok := pc.mutation.PaymentGatewayProvider(); !ok { + return &ValidationError{Name: "payment_gateway_provider", err: errors.New(`ent: missing required field "Payment.payment_gateway_provider"`)} + } if v, ok := pc.mutation.PaymentGatewayProvider(); ok { if err := v.Validate(); err != nil { return &ValidationError{Name: "payment_gateway_provider", err: fmt.Errorf(`ent: validator failed for field "Payment.payment_gateway_provider": %w`, err)} @@ -473,7 +484,7 @@ func (pc *PaymentCreate) createSpec() (*Payment, *sqlgraph.CreateSpec) { } if value, ok := pc.mutation.PaymentMethodType(); ok { _spec.SetField(payment.FieldPaymentMethodType, field.TypeString, value) - _node.PaymentMethodType = value + _node.PaymentMethodType = &value } if value, ok := pc.mutation.PaymentMethodID(); ok { _spec.SetField(payment.FieldPaymentMethodID, field.TypeString, value) @@ -481,7 +492,7 @@ func (pc *PaymentCreate) createSpec() (*Payment, *sqlgraph.CreateSpec) { } if value, ok := pc.mutation.PaymentGatewayProvider(); ok { _spec.SetField(payment.FieldPaymentGatewayProvider, field.TypeString, value) - _node.PaymentGatewayProvider = &value + _node.PaymentGatewayProvider = value } if value, ok := pc.mutation.GatewayPaymentID(); ok { _spec.SetField(payment.FieldGatewayPaymentID, field.TypeString, value) diff --git a/ent/payment_update.go b/ent/payment_update.go index 664f1ce..5217d74 100644 --- a/ent/payment_update.go +++ b/ent/payment_update.go @@ -71,34 +71,6 @@ func (pu *PaymentUpdate) ClearUpdatedBy() *PaymentUpdate { return pu } -// SetDestinationType sets the "destination_type" field. -func (pu *PaymentUpdate) SetDestinationType(tdt types.PaymentDestinationType) *PaymentUpdate { - pu.mutation.SetDestinationType(tdt) - return pu -} - -// SetNillableDestinationType sets the "destination_type" field if the given value is not nil. -func (pu *PaymentUpdate) SetNillableDestinationType(tdt *types.PaymentDestinationType) *PaymentUpdate { - if tdt != nil { - pu.SetDestinationType(*tdt) - } - return pu -} - -// SetDestinationID sets the "destination_id" field. -func (pu *PaymentUpdate) SetDestinationID(s string) *PaymentUpdate { - pu.mutation.SetDestinationID(s) - return pu -} - -// SetNillableDestinationID sets the "destination_id" field if the given value is not nil. -func (pu *PaymentUpdate) SetNillableDestinationID(s *string) *PaymentUpdate { - if s != nil { - pu.SetDestinationID(*s) - } - return pu -} - // SetPaymentMethodType sets the "payment_method_type" field. func (pu *PaymentUpdate) SetPaymentMethodType(tmt types.PaymentMethodType) *PaymentUpdate { pu.mutation.SetPaymentMethodType(tmt) @@ -113,6 +85,12 @@ func (pu *PaymentUpdate) SetNillablePaymentMethodType(tmt *types.PaymentMethodTy return pu } +// ClearPaymentMethodType clears the value of the "payment_method_type" field. +func (pu *PaymentUpdate) ClearPaymentMethodType() *PaymentUpdate { + pu.mutation.ClearPaymentMethodType() + return pu +} + // SetPaymentMethodID sets the "payment_method_id" field. func (pu *PaymentUpdate) SetPaymentMethodID(s string) *PaymentUpdate { pu.mutation.SetPaymentMethodID(s) @@ -133,26 +111,6 @@ func (pu *PaymentUpdate) ClearPaymentMethodID() *PaymentUpdate { return pu } -// SetPaymentGatewayProvider sets the "payment_gateway_provider" field. -func (pu *PaymentUpdate) SetPaymentGatewayProvider(tgp types.PaymentGatewayProvider) *PaymentUpdate { - pu.mutation.SetPaymentGatewayProvider(tgp) - return pu -} - -// SetNillablePaymentGatewayProvider sets the "payment_gateway_provider" field if the given value is not nil. -func (pu *PaymentUpdate) SetNillablePaymentGatewayProvider(tgp *types.PaymentGatewayProvider) *PaymentUpdate { - if tgp != nil { - pu.SetPaymentGatewayProvider(*tgp) - } - return pu -} - -// ClearPaymentGatewayProvider clears the value of the "payment_gateway_provider" field. -func (pu *PaymentUpdate) ClearPaymentGatewayProvider() *PaymentUpdate { - pu.mutation.ClearPaymentGatewayProvider() - return pu -} - // SetGatewayPaymentID sets the "gateway_payment_id" field. func (pu *PaymentUpdate) SetGatewayPaymentID(s string) *PaymentUpdate { pu.mutation.SetGatewayPaymentID(s) @@ -386,24 +344,9 @@ func (pu *PaymentUpdate) defaults() { // check runs all checks and user-defined validators on the builder. func (pu *PaymentUpdate) check() error { - if v, ok := pu.mutation.DestinationType(); ok { - if err := payment.DestinationTypeValidator(string(v)); err != nil { - return &ValidationError{Name: "destination_type", err: fmt.Errorf(`ent: validator failed for field "Payment.destination_type": %w`, err)} - } - } - if v, ok := pu.mutation.DestinationID(); ok { - if err := payment.DestinationIDValidator(v); err != nil { - return &ValidationError{Name: "destination_id", err: fmt.Errorf(`ent: validator failed for field "Payment.destination_id": %w`, err)} - } - } if v, ok := pu.mutation.PaymentMethodType(); ok { - if err := payment.PaymentMethodTypeValidator(string(v)); err != nil { - return &ValidationError{Name: "payment_method_type", err: fmt.Errorf(`ent: validator failed for field "Payment.payment_method_type": %w`, err)} - } - } - if v, ok := pu.mutation.PaymentGatewayProvider(); ok { if err := v.Validate(); err != nil { - return &ValidationError{Name: "payment_gateway_provider", err: fmt.Errorf(`ent: validator failed for field "Payment.payment_gateway_provider": %w`, err)} + return &ValidationError{Name: "payment_method_type", err: fmt.Errorf(`ent: validator failed for field "Payment.payment_method_type": %w`, err)} } } if v, ok := pu.mutation.PaymentStatus(); ok { @@ -441,27 +384,18 @@ func (pu *PaymentUpdate) sqlSave(ctx context.Context) (n int, err error) { if pu.mutation.UpdatedByCleared() { _spec.ClearField(payment.FieldUpdatedBy, field.TypeString) } - if value, ok := pu.mutation.DestinationType(); ok { - _spec.SetField(payment.FieldDestinationType, field.TypeString, value) - } - if value, ok := pu.mutation.DestinationID(); ok { - _spec.SetField(payment.FieldDestinationID, field.TypeString, value) - } if value, ok := pu.mutation.PaymentMethodType(); ok { _spec.SetField(payment.FieldPaymentMethodType, field.TypeString, value) } + if pu.mutation.PaymentMethodTypeCleared() { + _spec.ClearField(payment.FieldPaymentMethodType, field.TypeString) + } if value, ok := pu.mutation.PaymentMethodID(); ok { _spec.SetField(payment.FieldPaymentMethodID, field.TypeString, value) } if pu.mutation.PaymentMethodIDCleared() { _spec.ClearField(payment.FieldPaymentMethodID, field.TypeString) } - if value, ok := pu.mutation.PaymentGatewayProvider(); ok { - _spec.SetField(payment.FieldPaymentGatewayProvider, field.TypeString, value) - } - if pu.mutation.PaymentGatewayProviderCleared() { - _spec.ClearField(payment.FieldPaymentGatewayProvider, field.TypeString) - } if value, ok := pu.mutation.GatewayPaymentID(); ok { _spec.SetField(payment.FieldGatewayPaymentID, field.TypeString, value) } @@ -612,34 +546,6 @@ func (puo *PaymentUpdateOne) ClearUpdatedBy() *PaymentUpdateOne { return puo } -// SetDestinationType sets the "destination_type" field. -func (puo *PaymentUpdateOne) SetDestinationType(tdt types.PaymentDestinationType) *PaymentUpdateOne { - puo.mutation.SetDestinationType(tdt) - return puo -} - -// SetNillableDestinationType sets the "destination_type" field if the given value is not nil. -func (puo *PaymentUpdateOne) SetNillableDestinationType(tdt *types.PaymentDestinationType) *PaymentUpdateOne { - if tdt != nil { - puo.SetDestinationType(*tdt) - } - return puo -} - -// SetDestinationID sets the "destination_id" field. -func (puo *PaymentUpdateOne) SetDestinationID(s string) *PaymentUpdateOne { - puo.mutation.SetDestinationID(s) - return puo -} - -// SetNillableDestinationID sets the "destination_id" field if the given value is not nil. -func (puo *PaymentUpdateOne) SetNillableDestinationID(s *string) *PaymentUpdateOne { - if s != nil { - puo.SetDestinationID(*s) - } - return puo -} - // SetPaymentMethodType sets the "payment_method_type" field. func (puo *PaymentUpdateOne) SetPaymentMethodType(tmt types.PaymentMethodType) *PaymentUpdateOne { puo.mutation.SetPaymentMethodType(tmt) @@ -654,6 +560,12 @@ func (puo *PaymentUpdateOne) SetNillablePaymentMethodType(tmt *types.PaymentMeth return puo } +// ClearPaymentMethodType clears the value of the "payment_method_type" field. +func (puo *PaymentUpdateOne) ClearPaymentMethodType() *PaymentUpdateOne { + puo.mutation.ClearPaymentMethodType() + return puo +} + // SetPaymentMethodID sets the "payment_method_id" field. func (puo *PaymentUpdateOne) SetPaymentMethodID(s string) *PaymentUpdateOne { puo.mutation.SetPaymentMethodID(s) @@ -674,26 +586,6 @@ func (puo *PaymentUpdateOne) ClearPaymentMethodID() *PaymentUpdateOne { return puo } -// SetPaymentGatewayProvider sets the "payment_gateway_provider" field. -func (puo *PaymentUpdateOne) SetPaymentGatewayProvider(tgp types.PaymentGatewayProvider) *PaymentUpdateOne { - puo.mutation.SetPaymentGatewayProvider(tgp) - return puo -} - -// SetNillablePaymentGatewayProvider sets the "payment_gateway_provider" field if the given value is not nil. -func (puo *PaymentUpdateOne) SetNillablePaymentGatewayProvider(tgp *types.PaymentGatewayProvider) *PaymentUpdateOne { - if tgp != nil { - puo.SetPaymentGatewayProvider(*tgp) - } - return puo -} - -// ClearPaymentGatewayProvider clears the value of the "payment_gateway_provider" field. -func (puo *PaymentUpdateOne) ClearPaymentGatewayProvider() *PaymentUpdateOne { - puo.mutation.ClearPaymentGatewayProvider() - return puo -} - // SetGatewayPaymentID sets the "gateway_payment_id" field. func (puo *PaymentUpdateOne) SetGatewayPaymentID(s string) *PaymentUpdateOne { puo.mutation.SetGatewayPaymentID(s) @@ -940,24 +832,9 @@ func (puo *PaymentUpdateOne) defaults() { // check runs all checks and user-defined validators on the builder. func (puo *PaymentUpdateOne) check() error { - if v, ok := puo.mutation.DestinationType(); ok { - if err := payment.DestinationTypeValidator(string(v)); err != nil { - return &ValidationError{Name: "destination_type", err: fmt.Errorf(`ent: validator failed for field "Payment.destination_type": %w`, err)} - } - } - if v, ok := puo.mutation.DestinationID(); ok { - if err := payment.DestinationIDValidator(v); err != nil { - return &ValidationError{Name: "destination_id", err: fmt.Errorf(`ent: validator failed for field "Payment.destination_id": %w`, err)} - } - } if v, ok := puo.mutation.PaymentMethodType(); ok { - if err := payment.PaymentMethodTypeValidator(string(v)); err != nil { - return &ValidationError{Name: "payment_method_type", err: fmt.Errorf(`ent: validator failed for field "Payment.payment_method_type": %w`, err)} - } - } - if v, ok := puo.mutation.PaymentGatewayProvider(); ok { if err := v.Validate(); err != nil { - return &ValidationError{Name: "payment_gateway_provider", err: fmt.Errorf(`ent: validator failed for field "Payment.payment_gateway_provider": %w`, err)} + return &ValidationError{Name: "payment_method_type", err: fmt.Errorf(`ent: validator failed for field "Payment.payment_method_type": %w`, err)} } } if v, ok := puo.mutation.PaymentStatus(); ok { @@ -1012,27 +889,18 @@ func (puo *PaymentUpdateOne) sqlSave(ctx context.Context) (_node *Payment, err e if puo.mutation.UpdatedByCleared() { _spec.ClearField(payment.FieldUpdatedBy, field.TypeString) } - if value, ok := puo.mutation.DestinationType(); ok { - _spec.SetField(payment.FieldDestinationType, field.TypeString, value) - } - if value, ok := puo.mutation.DestinationID(); ok { - _spec.SetField(payment.FieldDestinationID, field.TypeString, value) - } if value, ok := puo.mutation.PaymentMethodType(); ok { _spec.SetField(payment.FieldPaymentMethodType, field.TypeString, value) } + if puo.mutation.PaymentMethodTypeCleared() { + _spec.ClearField(payment.FieldPaymentMethodType, field.TypeString) + } if value, ok := puo.mutation.PaymentMethodID(); ok { _spec.SetField(payment.FieldPaymentMethodID, field.TypeString, value) } if puo.mutation.PaymentMethodIDCleared() { _spec.ClearField(payment.FieldPaymentMethodID, field.TypeString) } - if value, ok := puo.mutation.PaymentGatewayProvider(); ok { - _spec.SetField(payment.FieldPaymentGatewayProvider, field.TypeString, value) - } - if puo.mutation.PaymentGatewayProviderCleared() { - _spec.ClearField(payment.FieldPaymentGatewayProvider, field.TypeString) - } if value, ok := puo.mutation.GatewayPaymentID(); ok { _spec.SetField(payment.FieldGatewayPaymentID, field.TypeString, value) } diff --git a/ent/predicate/predicate.go b/ent/predicate/predicate.go index 2ca2e5d..fa909d5 100644 --- a/ent/predicate/predicate.go +++ b/ent/predicate/predicate.go @@ -6,6 +6,12 @@ import ( "entgo.io/ent/dialect/sql" ) +// Cart is the predicate function for cart builders. +type Cart func(*sql.Selector) + +// CartLineItems is the predicate function for cartlineitems builders. +type CartLineItems func(*sql.Selector) + // Category is the predicate function for category builders. type Category func(*sql.Selector) @@ -18,6 +24,15 @@ type FileUpload func(*sql.Selector) // Internship is the predicate function for internship builders. type Internship func(*sql.Selector) +// InternshipBatch is the predicate function for internshipbatch builders. +type InternshipBatch func(*sql.Selector) + +// InternshipEnrollment is the predicate function for internshipenrollment builders. +type InternshipEnrollment func(*sql.Selector) + +// Order is the predicate function for order builders. +type Order func(*sql.Selector) + // Payment is the predicate function for payment builders. type Payment func(*sql.Selector) diff --git a/ent/runtime.go b/ent/runtime.go index 3094c16..82037db 100644 --- a/ent/runtime.go +++ b/ent/runtime.go @@ -5,10 +5,14 @@ package ent import ( "time" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" "github.com/omkar273/codegeeky/ent/category" "github.com/omkar273/codegeeky/ent/discount" "github.com/omkar273/codegeeky/ent/fileupload" "github.com/omkar273/codegeeky/ent/internship" + "github.com/omkar273/codegeeky/ent/internshipbatch" + "github.com/omkar273/codegeeky/ent/internshipenrollment" "github.com/omkar273/codegeeky/ent/payment" "github.com/omkar273/codegeeky/ent/paymentattempt" "github.com/omkar273/codegeeky/ent/schema" @@ -21,9 +25,129 @@ import ( // (default values, validators, hooks and policies) and stitches it // to their package variables. func init() { + cartMixin := schema.Cart{}.Mixin() + cartMixinFields0 := cartMixin[0].Fields() + _ = cartMixinFields0 + cartMixinFields1 := cartMixin[1].Fields() + _ = cartMixinFields1 + cartFields := schema.Cart{}.Fields() + _ = cartFields + // cartDescStatus is the schema descriptor for status field. + cartDescStatus := cartMixinFields0[0].Descriptor() + // cart.DefaultStatus holds the default value on creation for the status field. + cart.DefaultStatus = cartDescStatus.Default.(string) + // cartDescCreatedAt is the schema descriptor for created_at field. + cartDescCreatedAt := cartMixinFields0[1].Descriptor() + // cart.DefaultCreatedAt holds the default value on creation for the created_at field. + cart.DefaultCreatedAt = cartDescCreatedAt.Default.(func() time.Time) + // cartDescUpdatedAt is the schema descriptor for updated_at field. + cartDescUpdatedAt := cartMixinFields0[2].Descriptor() + // cart.DefaultUpdatedAt holds the default value on creation for the updated_at field. + cart.DefaultUpdatedAt = cartDescUpdatedAt.Default.(func() time.Time) + // cart.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + cart.UpdateDefaultUpdatedAt = cartDescUpdatedAt.UpdateDefault.(func() time.Time) + // cartDescMetadata is the schema descriptor for metadata field. + cartDescMetadata := cartMixinFields1[0].Descriptor() + // cart.DefaultMetadata holds the default value on creation for the metadata field. + cart.DefaultMetadata = cartDescMetadata.Default.(map[string]string) + // cartDescUserID is the schema descriptor for user_id field. + cartDescUserID := cartFields[1].Descriptor() + // cart.UserIDValidator is a validator for the "user_id" field. It is called by the builders before save. + cart.UserIDValidator = cartDescUserID.Validators[0].(func(string) error) + // cartDescType is the schema descriptor for type field. + cartDescType := cartFields[2].Descriptor() + // cart.TypeValidator is a validator for the "type" field. It is called by the builders before save. + cart.TypeValidator = cartDescType.Validators[0].(func(string) error) + // cartDescSubtotal is the schema descriptor for subtotal field. + cartDescSubtotal := cartFields[3].Descriptor() + // cart.DefaultSubtotal holds the default value on creation for the subtotal field. + cart.DefaultSubtotal = cartDescSubtotal.Default.(decimal.Decimal) + // cartDescDiscountAmount is the schema descriptor for discount_amount field. + cartDescDiscountAmount := cartFields[4].Descriptor() + // cart.DefaultDiscountAmount holds the default value on creation for the discount_amount field. + cart.DefaultDiscountAmount = cartDescDiscountAmount.Default.(decimal.Decimal) + // cartDescTaxAmount is the schema descriptor for tax_amount field. + cartDescTaxAmount := cartFields[5].Descriptor() + // cart.DefaultTaxAmount holds the default value on creation for the tax_amount field. + cart.DefaultTaxAmount = cartDescTaxAmount.Default.(decimal.Decimal) + // cartDescTotal is the schema descriptor for total field. + cartDescTotal := cartFields[6].Descriptor() + // cart.DefaultTotal holds the default value on creation for the total field. + cart.DefaultTotal = cartDescTotal.Default.(decimal.Decimal) + // cartDescID is the schema descriptor for id field. + cartDescID := cartFields[0].Descriptor() + // cart.DefaultID holds the default value on creation for the id field. + cart.DefaultID = cartDescID.Default.(func() string) + cartlineitemsMixin := schema.CartLineItems{}.Mixin() + cartlineitemsMixinFields0 := cartlineitemsMixin[0].Fields() + _ = cartlineitemsMixinFields0 + cartlineitemsMixinFields1 := cartlineitemsMixin[1].Fields() + _ = cartlineitemsMixinFields1 + cartlineitemsFields := schema.CartLineItems{}.Fields() + _ = cartlineitemsFields + // cartlineitemsDescStatus is the schema descriptor for status field. + cartlineitemsDescStatus := cartlineitemsMixinFields0[0].Descriptor() + // cartlineitems.DefaultStatus holds the default value on creation for the status field. + cartlineitems.DefaultStatus = cartlineitemsDescStatus.Default.(string) + // cartlineitemsDescCreatedAt is the schema descriptor for created_at field. + cartlineitemsDescCreatedAt := cartlineitemsMixinFields0[1].Descriptor() + // cartlineitems.DefaultCreatedAt holds the default value on creation for the created_at field. + cartlineitems.DefaultCreatedAt = cartlineitemsDescCreatedAt.Default.(func() time.Time) + // cartlineitemsDescUpdatedAt is the schema descriptor for updated_at field. + cartlineitemsDescUpdatedAt := cartlineitemsMixinFields0[2].Descriptor() + // cartlineitems.DefaultUpdatedAt holds the default value on creation for the updated_at field. + cartlineitems.DefaultUpdatedAt = cartlineitemsDescUpdatedAt.Default.(func() time.Time) + // cartlineitems.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + cartlineitems.UpdateDefaultUpdatedAt = cartlineitemsDescUpdatedAt.UpdateDefault.(func() time.Time) + // cartlineitemsDescMetadata is the schema descriptor for metadata field. + cartlineitemsDescMetadata := cartlineitemsMixinFields1[0].Descriptor() + // cartlineitems.DefaultMetadata holds the default value on creation for the metadata field. + cartlineitems.DefaultMetadata = cartlineitemsDescMetadata.Default.(map[string]string) + // cartlineitemsDescCartID is the schema descriptor for cart_id field. + cartlineitemsDescCartID := cartlineitemsFields[1].Descriptor() + // cartlineitems.CartIDValidator is a validator for the "cart_id" field. It is called by the builders before save. + cartlineitems.CartIDValidator = cartlineitemsDescCartID.Validators[0].(func(string) error) + // cartlineitemsDescEntityID is the schema descriptor for entity_id field. + cartlineitemsDescEntityID := cartlineitemsFields[2].Descriptor() + // cartlineitems.EntityIDValidator is a validator for the "entity_id" field. It is called by the builders before save. + cartlineitems.EntityIDValidator = cartlineitemsDescEntityID.Validators[0].(func(string) error) + // cartlineitemsDescEntityType is the schema descriptor for entity_type field. + cartlineitemsDescEntityType := cartlineitemsFields[3].Descriptor() + // cartlineitems.EntityTypeValidator is a validator for the "entity_type" field. It is called by the builders before save. + cartlineitems.EntityTypeValidator = cartlineitemsDescEntityType.Validators[0].(func(string) error) + // cartlineitemsDescQuantity is the schema descriptor for quantity field. + cartlineitemsDescQuantity := cartlineitemsFields[4].Descriptor() + // cartlineitems.DefaultQuantity holds the default value on creation for the quantity field. + cartlineitems.DefaultQuantity = cartlineitemsDescQuantity.Default.(int) + // cartlineitemsDescPerUnitPrice is the schema descriptor for per_unit_price field. + cartlineitemsDescPerUnitPrice := cartlineitemsFields[5].Descriptor() + // cartlineitems.DefaultPerUnitPrice holds the default value on creation for the per_unit_price field. + cartlineitems.DefaultPerUnitPrice = cartlineitemsDescPerUnitPrice.Default.(decimal.Decimal) + // cartlineitemsDescTaxAmount is the schema descriptor for tax_amount field. + cartlineitemsDescTaxAmount := cartlineitemsFields[6].Descriptor() + // cartlineitems.DefaultTaxAmount holds the default value on creation for the tax_amount field. + cartlineitems.DefaultTaxAmount = cartlineitemsDescTaxAmount.Default.(decimal.Decimal) + // cartlineitemsDescDiscountAmount is the schema descriptor for discount_amount field. + cartlineitemsDescDiscountAmount := cartlineitemsFields[7].Descriptor() + // cartlineitems.DefaultDiscountAmount holds the default value on creation for the discount_amount field. + cartlineitems.DefaultDiscountAmount = cartlineitemsDescDiscountAmount.Default.(decimal.Decimal) + // cartlineitemsDescSubtotal is the schema descriptor for subtotal field. + cartlineitemsDescSubtotal := cartlineitemsFields[8].Descriptor() + // cartlineitems.DefaultSubtotal holds the default value on creation for the subtotal field. + cartlineitems.DefaultSubtotal = cartlineitemsDescSubtotal.Default.(decimal.Decimal) + // cartlineitemsDescTotal is the schema descriptor for total field. + cartlineitemsDescTotal := cartlineitemsFields[9].Descriptor() + // cartlineitems.DefaultTotal holds the default value on creation for the total field. + cartlineitems.DefaultTotal = cartlineitemsDescTotal.Default.(decimal.Decimal) + // cartlineitemsDescID is the schema descriptor for id field. + cartlineitemsDescID := cartlineitemsFields[0].Descriptor() + // cartlineitems.DefaultID holds the default value on creation for the id field. + cartlineitems.DefaultID = cartlineitemsDescID.Default.(func() string) categoryMixin := schema.Category{}.Mixin() categoryMixinFields0 := categoryMixin[0].Fields() _ = categoryMixinFields0 + categoryMixinFields1 := categoryMixin[1].Fields() + _ = categoryMixinFields1 categoryFields := schema.Category{}.Fields() _ = categoryFields // categoryDescStatus is the schema descriptor for status field. @@ -40,6 +164,10 @@ func init() { category.DefaultUpdatedAt = categoryDescUpdatedAt.Default.(func() time.Time) // category.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. category.UpdateDefaultUpdatedAt = categoryDescUpdatedAt.UpdateDefault.(func() time.Time) + // categoryDescMetadata is the schema descriptor for metadata field. + categoryDescMetadata := categoryMixinFields1[0].Descriptor() + // category.DefaultMetadata holds the default value on creation for the metadata field. + category.DefaultMetadata = categoryDescMetadata.Default.(map[string]string) // categoryDescName is the schema descriptor for name field. categoryDescName := categoryFields[1].Descriptor() // category.NameValidator is a validator for the "name" field. It is called by the builders before save. @@ -197,10 +325,114 @@ func init() { internshipDescMode := internshipFields[6].Descriptor() // internship.ModeValidator is a validator for the "mode" field. It is called by the builders before save. internship.ModeValidator = internshipDescMode.Validators[0].(func(string) error) + // internshipDescSubtotal is the schema descriptor for subtotal field. + internshipDescSubtotal := internshipFields[15].Descriptor() + // internship.DefaultSubtotal holds the default value on creation for the subtotal field. + internship.DefaultSubtotal = internshipDescSubtotal.Default.(decimal.Decimal) + // internshipDescTotal is the schema descriptor for total field. + internshipDescTotal := internshipFields[16].Descriptor() + // internship.DefaultTotal holds the default value on creation for the total field. + internship.DefaultTotal = internshipDescTotal.Default.(decimal.Decimal) // internshipDescID is the schema descriptor for id field. internshipDescID := internshipFields[0].Descriptor() // internship.DefaultID holds the default value on creation for the id field. internship.DefaultID = internshipDescID.Default.(func() string) + internshipbatchMixin := schema.InternshipBatch{}.Mixin() + internshipbatchMixinFields0 := internshipbatchMixin[0].Fields() + _ = internshipbatchMixinFields0 + internshipbatchMixinFields1 := internshipbatchMixin[1].Fields() + _ = internshipbatchMixinFields1 + internshipbatchFields := schema.InternshipBatch{}.Fields() + _ = internshipbatchFields + // internshipbatchDescStatus is the schema descriptor for status field. + internshipbatchDescStatus := internshipbatchMixinFields0[0].Descriptor() + // internshipbatch.DefaultStatus holds the default value on creation for the status field. + internshipbatch.DefaultStatus = internshipbatchDescStatus.Default.(string) + // internshipbatchDescCreatedAt is the schema descriptor for created_at field. + internshipbatchDescCreatedAt := internshipbatchMixinFields0[1].Descriptor() + // internshipbatch.DefaultCreatedAt holds the default value on creation for the created_at field. + internshipbatch.DefaultCreatedAt = internshipbatchDescCreatedAt.Default.(func() time.Time) + // internshipbatchDescUpdatedAt is the schema descriptor for updated_at field. + internshipbatchDescUpdatedAt := internshipbatchMixinFields0[2].Descriptor() + // internshipbatch.DefaultUpdatedAt holds the default value on creation for the updated_at field. + internshipbatch.DefaultUpdatedAt = internshipbatchDescUpdatedAt.Default.(func() time.Time) + // internshipbatch.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + internshipbatch.UpdateDefaultUpdatedAt = internshipbatchDescUpdatedAt.UpdateDefault.(func() time.Time) + // internshipbatchDescMetadata is the schema descriptor for metadata field. + internshipbatchDescMetadata := internshipbatchMixinFields1[0].Descriptor() + // internshipbatch.DefaultMetadata holds the default value on creation for the metadata field. + internshipbatch.DefaultMetadata = internshipbatchDescMetadata.Default.(map[string]string) + // internshipbatchDescInternshipID is the schema descriptor for internship_id field. + internshipbatchDescInternshipID := internshipbatchFields[1].Descriptor() + // internshipbatch.InternshipIDValidator is a validator for the "internship_id" field. It is called by the builders before save. + internshipbatch.InternshipIDValidator = internshipbatchDescInternshipID.Validators[0].(func(string) error) + // internshipbatchDescName is the schema descriptor for name field. + internshipbatchDescName := internshipbatchFields[2].Descriptor() + // internshipbatch.NameValidator is a validator for the "name" field. It is called by the builders before save. + internshipbatch.NameValidator = internshipbatchDescName.Validators[0].(func(string) error) + // internshipbatchDescBatchStatus is the schema descriptor for batch_status field. + internshipbatchDescBatchStatus := internshipbatchFields[6].Descriptor() + // internshipbatch.DefaultBatchStatus holds the default value on creation for the batch_status field. + internshipbatch.DefaultBatchStatus = internshipbatchDescBatchStatus.Default.(string) + // internshipbatch.BatchStatusValidator is a validator for the "batch_status" field. It is called by the builders before save. + internshipbatch.BatchStatusValidator = internshipbatchDescBatchStatus.Validators[0].(func(string) error) + // internshipbatchDescID is the schema descriptor for id field. + internshipbatchDescID := internshipbatchFields[0].Descriptor() + // internshipbatch.DefaultID holds the default value on creation for the id field. + internshipbatch.DefaultID = internshipbatchDescID.Default.(func() string) + internshipenrollmentMixin := schema.InternshipEnrollment{}.Mixin() + internshipenrollmentMixinFields0 := internshipenrollmentMixin[0].Fields() + _ = internshipenrollmentMixinFields0 + internshipenrollmentMixinFields1 := internshipenrollmentMixin[1].Fields() + _ = internshipenrollmentMixinFields1 + internshipenrollmentFields := schema.InternshipEnrollment{}.Fields() + _ = internshipenrollmentFields + // internshipenrollmentDescStatus is the schema descriptor for status field. + internshipenrollmentDescStatus := internshipenrollmentMixinFields0[0].Descriptor() + // internshipenrollment.DefaultStatus holds the default value on creation for the status field. + internshipenrollment.DefaultStatus = internshipenrollmentDescStatus.Default.(string) + // internshipenrollmentDescCreatedAt is the schema descriptor for created_at field. + internshipenrollmentDescCreatedAt := internshipenrollmentMixinFields0[1].Descriptor() + // internshipenrollment.DefaultCreatedAt holds the default value on creation for the created_at field. + internshipenrollment.DefaultCreatedAt = internshipenrollmentDescCreatedAt.Default.(func() time.Time) + // internshipenrollmentDescUpdatedAt is the schema descriptor for updated_at field. + internshipenrollmentDescUpdatedAt := internshipenrollmentMixinFields0[2].Descriptor() + // internshipenrollment.DefaultUpdatedAt holds the default value on creation for the updated_at field. + internshipenrollment.DefaultUpdatedAt = internshipenrollmentDescUpdatedAt.Default.(func() time.Time) + // internshipenrollment.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + internshipenrollment.UpdateDefaultUpdatedAt = internshipenrollmentDescUpdatedAt.UpdateDefault.(func() time.Time) + // internshipenrollmentDescMetadata is the schema descriptor for metadata field. + internshipenrollmentDescMetadata := internshipenrollmentMixinFields1[0].Descriptor() + // internshipenrollment.DefaultMetadata holds the default value on creation for the metadata field. + internshipenrollment.DefaultMetadata = internshipenrollmentDescMetadata.Default.(map[string]string) + // internshipenrollmentDescUserID is the schema descriptor for user_id field. + internshipenrollmentDescUserID := internshipenrollmentFields[1].Descriptor() + // internshipenrollment.UserIDValidator is a validator for the "user_id" field. It is called by the builders before save. + internshipenrollment.UserIDValidator = internshipenrollmentDescUserID.Validators[0].(func(string) error) + // internshipenrollmentDescInternshipID is the schema descriptor for internship_id field. + internshipenrollmentDescInternshipID := internshipenrollmentFields[2].Descriptor() + // internshipenrollment.InternshipIDValidator is a validator for the "internship_id" field. It is called by the builders before save. + internshipenrollment.InternshipIDValidator = internshipenrollmentDescInternshipID.Validators[0].(func(string) error) + // internshipenrollmentDescInternshipBatchID is the schema descriptor for internship_batch_id field. + internshipenrollmentDescInternshipBatchID := internshipenrollmentFields[3].Descriptor() + // internshipenrollment.InternshipBatchIDValidator is a validator for the "internship_batch_id" field. It is called by the builders before save. + internshipenrollment.InternshipBatchIDValidator = internshipenrollmentDescInternshipBatchID.Validators[0].(func(string) error) + // internshipenrollmentDescEnrollmentStatus is the schema descriptor for enrollment_status field. + internshipenrollmentDescEnrollmentStatus := internshipenrollmentFields[4].Descriptor() + // internshipenrollment.DefaultEnrollmentStatus holds the default value on creation for the enrollment_status field. + internshipenrollment.DefaultEnrollmentStatus = types.InternshipEnrollmentStatus(internshipenrollmentDescEnrollmentStatus.Default.(string)) + // internshipenrollment.EnrollmentStatusValidator is a validator for the "enrollment_status" field. It is called by the builders before save. + internshipenrollment.EnrollmentStatusValidator = internshipenrollmentDescEnrollmentStatus.Validators[0].(func(string) error) + // internshipenrollmentDescPaymentStatus is the schema descriptor for payment_status field. + internshipenrollmentDescPaymentStatus := internshipenrollmentFields[5].Descriptor() + // internshipenrollment.DefaultPaymentStatus holds the default value on creation for the payment_status field. + internshipenrollment.DefaultPaymentStatus = types.PaymentStatus(internshipenrollmentDescPaymentStatus.Default.(string)) + // internshipenrollment.PaymentStatusValidator is a validator for the "payment_status" field. It is called by the builders before save. + internshipenrollment.PaymentStatusValidator = internshipenrollmentDescPaymentStatus.Validators[0].(func(string) error) + // internshipenrollmentDescID is the schema descriptor for id field. + internshipenrollmentDescID := internshipenrollmentFields[0].Descriptor() + // internshipenrollment.DefaultID holds the default value on creation for the id field. + internshipenrollment.DefaultID = internshipenrollmentDescID.Default.(func() string) paymentMixin := schema.Payment{}.Mixin() paymentMixinFields0 := paymentMixin[0].Fields() _ = paymentMixinFields0 @@ -220,18 +452,6 @@ func init() { payment.DefaultUpdatedAt = paymentDescUpdatedAt.Default.(func() time.Time) // payment.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. payment.UpdateDefaultUpdatedAt = paymentDescUpdatedAt.UpdateDefault.(func() time.Time) - // paymentDescDestinationType is the schema descriptor for destination_type field. - paymentDescDestinationType := paymentFields[2].Descriptor() - // payment.DestinationTypeValidator is a validator for the "destination_type" field. It is called by the builders before save. - payment.DestinationTypeValidator = paymentDescDestinationType.Validators[0].(func(string) error) - // paymentDescDestinationID is the schema descriptor for destination_id field. - paymentDescDestinationID := paymentFields[3].Descriptor() - // payment.DestinationIDValidator is a validator for the "destination_id" field. It is called by the builders before save. - payment.DestinationIDValidator = paymentDescDestinationID.Validators[0].(func(string) error) - // paymentDescPaymentMethodType is the schema descriptor for payment_method_type field. - paymentDescPaymentMethodType := paymentFields[4].Descriptor() - // payment.PaymentMethodTypeValidator is a validator for the "payment_method_type" field. It is called by the builders before save. - payment.PaymentMethodTypeValidator = paymentDescPaymentMethodType.Validators[0].(func(string) error) // paymentDescAmount is the schema descriptor for amount field. paymentDescAmount := paymentFields[8].Descriptor() // payment.DefaultAmount holds the default value on creation for the amount field. @@ -242,12 +462,18 @@ func init() { payment.CurrencyValidator = paymentDescCurrency.Validators[0].(func(string) error) // paymentDescPaymentStatus is the schema descriptor for payment_status field. paymentDescPaymentStatus := paymentFields[10].Descriptor() + // payment.DefaultPaymentStatus holds the default value on creation for the payment_status field. + payment.DefaultPaymentStatus = types.PaymentStatus(paymentDescPaymentStatus.Default.(string)) // payment.PaymentStatusValidator is a validator for the "payment_status" field. It is called by the builders before save. payment.PaymentStatusValidator = paymentDescPaymentStatus.Validators[0].(func(string) error) // paymentDescTrackAttempts is the schema descriptor for track_attempts field. paymentDescTrackAttempts := paymentFields[11].Descriptor() // payment.DefaultTrackAttempts holds the default value on creation for the track_attempts field. payment.DefaultTrackAttempts = paymentDescTrackAttempts.Default.(bool) + // paymentDescMetadata is the schema descriptor for metadata field. + paymentDescMetadata := paymentFields[12].Descriptor() + // payment.DefaultMetadata holds the default value on creation for the metadata field. + payment.DefaultMetadata = paymentDescMetadata.Default.(map[string]string) paymentattemptMixin := schema.PaymentAttempt{}.Mixin() paymentattemptMixinFields0 := paymentattemptMixin[0].Fields() _ = paymentattemptMixinFields0 diff --git a/ent/schema/cart.go b/ent/schema/cart.go new file mode 100644 index 0000000..7d0c697 --- /dev/null +++ b/ent/schema/cart.go @@ -0,0 +1,101 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + baseMixin "github.com/omkar273/codegeeky/ent/mixin" + "github.com/omkar273/codegeeky/internal/types" + "github.com/shopspring/decimal" +) + +// Cart holds the schema definition for the Cart entity. +type Cart struct { + ent.Schema +} + +// Mixin of the Cart. +func (Cart) Mixin() []ent.Mixin { + return []ent.Mixin{ + baseMixin.BaseMixin{}, + baseMixin.MetadataMixin{}, + } +} + +// Fields of the Cart. +func (Cart) Fields() []ent.Field { + return []ent.Field{ + field.String("id"). + SchemaType(map[string]string{ + "postgres": "varchar(255)", + }). + DefaultFunc(func() string { + return types.GenerateUUIDWithPrefix(types.UUID_PREFIX_CART) + }). + Immutable(), + field.String("user_id"). + NotEmpty(). + Immutable(), + + field.String("type"). + SchemaType(map[string]string{ + "postgres": "varchar(255)", + }). + NotEmpty(). + Immutable(), + + field.Other("subtotal", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero). + Immutable(), + + field.Other("discount_amount", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero). + Immutable(), + + field.Other("tax_amount", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero). + Immutable(), + + field.Other("total", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero). + Immutable(), + + field.Time("expires_at"). + SchemaType(map[string]string{ + "postgres": "timestamp", + }). + Immutable(), + } +} + +func (Cart) Edges() []ent.Edge { + return []ent.Edge{ + // CartLineItems + // one cart can have many line items + // one line item can have only one cart + edge.To("line_items", CartLineItems.Type), + + // defines a many-to-one relationship between Cart and User + // one user can have many carts + // one cart can have only one user + edge.From("user", User.Type). + Ref("carts"). + Unique(). + Required(). + Immutable(). + // Here we are mapping the user_id field to the user_id column in the users table + Field("user_id"), + } +} diff --git a/ent/schema/cart_line_items.go b/ent/schema/cart_line_items.go new file mode 100644 index 0000000..bafad96 --- /dev/null +++ b/ent/schema/cart_line_items.go @@ -0,0 +1,102 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + baseMixin "github.com/omkar273/codegeeky/ent/mixin" + "github.com/omkar273/codegeeky/internal/types" + "github.com/shopspring/decimal" +) + +// CartLineItems holds the schema definition for the CartLineItems entity. +type CartLineItems struct { + ent.Schema +} + +// Mixin of the CartLineItems. +func (CartLineItems) Mixin() []ent.Mixin { + return []ent.Mixin{ + baseMixin.BaseMixin{}, + baseMixin.MetadataMixin{}, + } +} + +// Fields of the CartLineItems. +func (CartLineItems) Fields() []ent.Field { + return []ent.Field{ + field.String("id"). + SchemaType(map[string]string{ + "postgres": "varchar(255)", + }). + DefaultFunc(func() string { + return types.GenerateUUIDWithPrefix(types.UUID_PREFIX_CART_LINE_ITEM) + }). + Immutable(), + + field.String("cart_id"). + SchemaType(map[string]string{ + "postgres": "varchar(255)", + }). + NotEmpty(). + Immutable(), + + field.String("entity_id"). + SchemaType(map[string]string{ + "postgres": "varchar(255)", + }). + NotEmpty(). + Immutable(), + + field.String("entity_type"). + SchemaType(map[string]string{ + "postgres": "varchar(255)", + }). + NotEmpty(). + Immutable(), + + field.Int("quantity"). + Default(1), + + field.Other("per_unit_price", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero), + + field.Other("tax_amount", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero), + + field.Other("discount_amount", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero), + + field.Other("subtotal", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero), + + field.Other("total", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "numeric", + }). + Default(decimal.Zero), + } +} + +func (CartLineItems) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("cart", Cart.Type). + Ref("line_items"). + Unique(). + Required(). + Immutable(). + Field("cart_id"), + } +} diff --git a/ent/schema/category.go b/ent/schema/category.go index e9c4202..95441e2 100644 --- a/ent/schema/category.go +++ b/ent/schema/category.go @@ -17,6 +17,7 @@ type Category struct { func (Category) Mixin() []ent.Mixin { return []ent.Mixin{ baseMixin.BaseMixin{}, + baseMixin.MetadataMixin{}, } } diff --git a/ent/schema/internship.go b/ent/schema/internship.go index 1df7017..ed0f9c3 100644 --- a/ent/schema/internship.go +++ b/ent/schema/internship.go @@ -102,7 +102,6 @@ func (Internship) Fields() []ent.Field { SchemaType(map[string]string{ "postgres": "decimal(10,2)", }). - Optional(). Comment("Price of the internship"), field.Other("flat_discount", decimal.Decimal{}). @@ -110,6 +109,7 @@ func (Internship) Fields() []ent.Field { "postgres": "decimal(10,2)", }). Optional(). + Nillable(). Comment("Flat discount on the internship"), field.Other("percentage_discount", decimal.Decimal{}). @@ -117,7 +117,22 @@ func (Internship) Fields() []ent.Field { "postgres": "decimal(10,2)", }). Optional(). + Nillable(). Comment("Percentage discount on the internship"), + + field.Other("subtotal", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "decimal(10,2)", + }). + Default(decimal.Zero). + Comment("Subtotal of the internship"), + + field.Other("total", decimal.Decimal{}). + SchemaType(map[string]string{ + "postgres": "decimal(10,2)", + }). + Default(decimal.Zero). + Comment("Price of the internship"), } } func (Internship) Edges() []ent.Edge { diff --git a/ent/schema/internship_batch.go b/ent/schema/internship_batch.go new file mode 100644 index 0000000..7228c14 --- /dev/null +++ b/ent/schema/internship_batch.go @@ -0,0 +1,63 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/field" + baseMixin "github.com/omkar273/codegeeky/ent/mixin" + "github.com/omkar273/codegeeky/internal/types" +) + +// InternshipBatch holds the schema definition for the InternshipBatch entity. +type InternshipBatch struct { + ent.Schema +} + +func (InternshipBatch) Mixin() []ent.Mixin { + return []ent.Mixin{ + baseMixin.BaseMixin{}, // includes created_at, updated_at + baseMixin.MetadataMixin{}, + } +} + +func (InternshipBatch) Fields() []ent.Field { + return []ent.Field{ + field.String("id"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + DefaultFunc(func() string { + return types.GenerateUUIDWithPrefix(types.UUID_PREFIX_INTERNSHIP_BATCH) + }). + Immutable(). + Unique(), + + field.String("internship_id"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + NotEmpty(). + Immutable(), + + // Name of the batch + field.String("name"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + NotEmpty(), + + // Description of the batch + field.String("description"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + Optional(), + + // Start date of the batch + field.Time("start_date"). + SchemaType(map[string]string{"postgres": "timestamp"}). + Optional(), + + // End date of the batch + field.Time("end_date"). + SchemaType(map[string]string{"postgres": "timestamp"}). + Optional(), + + // Status of the batch + field.String("batch_status"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + Default(string(types.InternshipBatchStatusUpcoming)). + NotEmpty(), + } +} diff --git a/ent/schema/internship_enrollment.go b/ent/schema/internship_enrollment.go new file mode 100644 index 0000000..6607026 --- /dev/null +++ b/ent/schema/internship_enrollment.go @@ -0,0 +1,95 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/field" + baseMixin "github.com/omkar273/codegeeky/ent/mixin" + "github.com/omkar273/codegeeky/internal/types" +) + +// InternshipEnrollment holds the schema definition for the InternshipEnrollment entity. +type InternshipEnrollment struct { + ent.Schema +} + +// Mixin of the InternshipEnrollment. +func (InternshipEnrollment) Mixin() []ent.Mixin { + return []ent.Mixin{ + baseMixin.BaseMixin{}, // includes created_at, updated_at + baseMixin.MetadataMixin{}, + } +} + +// Fields of the InternshipEnrollment. +func (InternshipEnrollment) Fields() []ent.Field { + return []ent.Field{ + field.String("id"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + DefaultFunc(func() string { + return types.GenerateUUIDWithPrefix(types.UUID_PREFIX_INTERNSHIP_ENROLLMENT) + }). + Immutable(), + + // Foreign keys + field.String("user_id"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + NotEmpty(), + + field.String("internship_id"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + NotEmpty(), + + field.String("internship_batch_id"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + NotEmpty(), + + // InternshipEnrollment status + field.String("enrollment_status"). + SchemaType(map[string]string{ + "postgres": "varchar(255)", + }). + GoType(types.InternshipEnrollmentStatus("")). + Default(string(types.InternshipEnrollmentStatusPending)). + NotEmpty(), + + // InternshipEnrollment payment status + field.String("payment_status"). + SchemaType(map[string]string{ + "postgres": "varchar(255)", + }). + GoType(types.PaymentStatus("")). + Default(string(types.PaymentStatusPending)). + NotEmpty(), + + // When InternshipEnrollment was confirmed + field.Time("enrolled_at"). + Optional(). + Nillable(), + + // Payment & refund linkage + // This is the internal payment id of caygnus not the actual provider id i.e. razorpay , stripe + field.String("payment_id"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + Optional(). + Nillable(), + + field.Time("refunded_at"). + Optional(). + Nillable(), + + // Optional reason for cancellation/refund + field.String("cancellation_reason"). + Optional(). + Nillable(), + + field.String("refund_reason"). + Optional(). + Nillable(), + + // Idempotency key + field.String("idempotency_key"). + SchemaType(map[string]string{"postgres": "varchar(255)"}). + Optional(). + Nillable(), + } +} diff --git a/ent/schema/order.go b/ent/schema/order.go new file mode 100644 index 0000000..828152b --- /dev/null +++ b/ent/schema/order.go @@ -0,0 +1,13 @@ +package schema + +import "entgo.io/ent" + +// Order holds the schema definition for the Order entity. +type Order struct { + ent.Schema +} + +// Fields of the Order. +func (Order) Fields() []ent.Field { + return nil +} diff --git a/ent/schema/payment.go b/ent/schema/payment.go index 5868fe2..a5d621c 100644 --- a/ent/schema/payment.go +++ b/ent/schema/payment.go @@ -32,52 +32,78 @@ func (Payment) Fields() []ent.Field { }). Unique(). Immutable(), + + // idempotency key + // Optional, for safe retries field.String("idempotency_key"). SchemaType(map[string]string{ "postgres": "varchar(50)", }). Unique(). Immutable(), + // destination type + // internship, subscription, charge, etc. field.String("destination_type"). GoType(types.PaymentDestinationType("")). SchemaType(map[string]string{ "postgres": "varchar(50)", }). - NotEmpty(), + Immutable(), + + // destination id + // ID of the destination (internship ID, subscription ID, etc.) field.String("destination_id"). SchemaType(map[string]string{ "postgres": "varchar(50)", }). - NotEmpty(), + Immutable(), + + // payment method type + // upi, card, wallet, etc. field.String("payment_method_type"). GoType(types.PaymentMethodType("")). SchemaType(map[string]string{ "postgres": "varchar(50)", }). - NotEmpty(), + Optional(). + Nillable(), + + // payment method id + // Saved method ID or token (optional) field.String("payment_method_id"). SchemaType(map[string]string{ "postgres": "varchar(50)", }). Optional(), + + // payment gateway provider + // razorpay, stripe, etc. field.String("payment_gateway_provider"). GoType(types.PaymentGatewayProvider("")). SchemaType(map[string]string{ "postgres": "varchar(50)", }). - Optional(). - Nillable(), + Immutable(), + + // gateway payment id + // Payment ID from the gateway field.String("gateway_payment_id"). SchemaType(map[string]string{ "postgres": "varchar(255)", }). Optional(). Nillable(), + + // amount + // Amount in smallest unit (e.g. paisa) field.Other("amount", decimal.Decimal{}). SchemaType(map[string]string{ "postgres": "numeric(20,8)", }). Default(decimal.Zero), + + // currency + // "INR", "USD", etc. field.String("currency"). GoType(types.Currency("")). SchemaType(map[string]string{ @@ -85,28 +111,51 @@ func (Payment) Fields() []ent.Field { }). NotEmpty(). Immutable(), + + // payment status + // pending, failed, succeeded, etc. field.String("payment_status"). GoType(types.PaymentStatus("")). SchemaType(map[string]string{ "postgres": "varchar(50)", }). + Default(string(types.PaymentStatusPending)). NotEmpty(), + + // track attempts + // Whether to track payment attempts field.Bool("track_attempts"). - Default(false), + Default(true), + + // metadata + // Additional tracking info (origin, cohort, etc.) field.JSON("metadata", map[string]string{}). Optional(). SchemaType(map[string]string{ "postgres": "jsonb", - }), + }). + Default(map[string]string{}), + + // succeeded at + // Time when the payment succeeded field.Time("succeeded_at"). Optional(). Nillable(), + + // failed at + // Time when the payment failed field.Time("failed_at"). Optional(). Nillable(), + + // refunded at + // Time when the payment was refunded field.Time("refunded_at"). Optional(). Nillable(), + + // error message + // Error message from the payment gateway field.String("error_message"). SchemaType(map[string]string{ "postgres": "text", @@ -119,6 +168,8 @@ func (Payment) Fields() []ent.Field { // Edges of the Payment. func (Payment) Edges() []ent.Edge { return []ent.Edge{ + // attempts + // Payment attempts edge.To("attempts", PaymentAttempt.Type), } } diff --git a/ent/schema/user.go b/ent/schema/user.go index c1a8f31..cdabb4f 100644 --- a/ent/schema/user.go +++ b/ent/schema/user.go @@ -2,6 +2,7 @@ package schema import ( "entgo.io/ent" + "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" "entgo.io/ent/schema/index" baseMixin "github.com/omkar273/codegeeky/ent/mixin" @@ -52,7 +53,12 @@ func (User) Fields() []ent.Field { } func (User) Edges() []ent.Edge { - return []ent.Edge{} + return []ent.Edge{ + // Cart + // one user can have many carts + // one cart can have only one user + edge.To("carts", Cart.Type), + } } // Indexes of the User. diff --git a/ent/tx.go b/ent/tx.go index ae24371..b641f6c 100644 --- a/ent/tx.go +++ b/ent/tx.go @@ -12,6 +12,10 @@ import ( // Tx is a transactional client that is created by calling Client.Tx(). type Tx struct { config + // Cart is the client for interacting with the Cart builders. + Cart *CartClient + // CartLineItems is the client for interacting with the CartLineItems builders. + CartLineItems *CartLineItemsClient // Category is the client for interacting with the Category builders. Category *CategoryClient // Discount is the client for interacting with the Discount builders. @@ -20,6 +24,12 @@ type Tx struct { FileUpload *FileUploadClient // Internship is the client for interacting with the Internship builders. Internship *InternshipClient + // InternshipBatch is the client for interacting with the InternshipBatch builders. + InternshipBatch *InternshipBatchClient + // InternshipEnrollment is the client for interacting with the InternshipEnrollment builders. + InternshipEnrollment *InternshipEnrollmentClient + // Order is the client for interacting with the Order builders. + Order *OrderClient // Payment is the client for interacting with the Payment builders. Payment *PaymentClient // PaymentAttempt is the client for interacting with the PaymentAttempt builders. @@ -157,10 +167,15 @@ func (tx *Tx) Client() *Client { } func (tx *Tx) init() { + tx.Cart = NewCartClient(tx.config) + tx.CartLineItems = NewCartLineItemsClient(tx.config) tx.Category = NewCategoryClient(tx.config) tx.Discount = NewDiscountClient(tx.config) tx.FileUpload = NewFileUploadClient(tx.config) tx.Internship = NewInternshipClient(tx.config) + tx.InternshipBatch = NewInternshipBatchClient(tx.config) + tx.InternshipEnrollment = NewInternshipEnrollmentClient(tx.config) + tx.Order = NewOrderClient(tx.config) tx.Payment = NewPaymentClient(tx.config) tx.PaymentAttempt = NewPaymentAttemptClient(tx.config) tx.User = NewUserClient(tx.config) @@ -173,7 +188,7 @@ func (tx *Tx) init() { // of them in order to commit or rollback the transaction. // // If a closed transaction is embedded in one of the generated entities, and the entity -// applies a query, for example: Category.QueryXXX(), the query will be executed +// applies a query, for example: Cart.QueryXXX(), the query will be executed // through the driver which created this transaction. // // Note that txDriver is not goroutine safe. diff --git a/ent/user.go b/ent/user.go index d755d0d..c4f4d22 100644 --- a/ent/user.go +++ b/ent/user.go @@ -34,10 +34,31 @@ type User struct { // PhoneNumber holds the value of the "phone_number" field. PhoneNumber string `json:"phone_number,omitempty"` // Role holds the value of the "role" field. - Role string `json:"role,omitempty"` + Role string `json:"role,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges UserEdges `json:"edges"` selectValues sql.SelectValues } +// UserEdges holds the relations/edges for other nodes in the graph. +type UserEdges struct { + // Carts holds the value of the carts edge. + Carts []*Cart `json:"carts,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// CartsOrErr returns the Carts value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) CartsOrErr() ([]*Cart, error) { + if e.loadedTypes[0] { + return e.Carts, nil + } + return nil, &NotLoadedError{edge: "carts"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -135,6 +156,11 @@ func (u *User) Value(name string) (ent.Value, error) { return u.selectValues.Get(name) } +// QueryCarts queries the "carts" edge of the User entity. +func (u *User) QueryCarts() *CartQuery { + return NewUserClient(u.config).QueryCarts(u) +} + // Update returns a builder for updating this User. // Note that you need to call User.Unwrap() before calling this method if this User // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/ent/user/user.go b/ent/user/user.go index 862349e..ae29d5d 100644 --- a/ent/user/user.go +++ b/ent/user/user.go @@ -6,6 +6,7 @@ import ( "time" "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" ) const ( @@ -31,8 +32,17 @@ const ( FieldPhoneNumber = "phone_number" // FieldRole holds the string denoting the role field in the database. FieldRole = "role" + // EdgeCarts holds the string denoting the carts edge name in mutations. + EdgeCarts = "carts" // Table holds the table name of the user in the database. Table = "users" + // CartsTable is the table that holds the carts relation/edge. + CartsTable = "carts" + // CartsInverseTable is the table name for the Cart entity. + // It exists in this package in order to avoid circular dependency with the "cart" package. + CartsInverseTable = "carts" + // CartsColumn is the table column denoting the carts relation/edge. + CartsColumn = "user_id" ) // Columns holds all SQL columns for user fields. @@ -130,3 +140,24 @@ func ByPhoneNumber(opts ...sql.OrderTermOption) OrderOption { func ByRole(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldRole, opts...).ToFunc() } + +// ByCartsCount orders the results by carts count. +func ByCartsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newCartsStep(), opts...) + } +} + +// ByCarts orders the results by carts terms. +func ByCarts(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newCartsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newCartsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(CartsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, CartsTable, CartsColumn), + ) +} diff --git a/ent/user/where.go b/ent/user/where.go index bd6049d..7ce43a9 100644 --- a/ent/user/where.go +++ b/ent/user/where.go @@ -6,6 +6,7 @@ import ( "time" "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" "github.com/omkar273/codegeeky/ent/predicate" ) @@ -664,6 +665,29 @@ func RoleContainsFold(v string) predicate.User { return predicate.User(sql.FieldContainsFold(FieldRole, v)) } +// HasCarts applies the HasEdge predicate on the "carts" edge. +func HasCarts() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, CartsTable, CartsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasCartsWith applies the HasEdge predicate on the "carts" edge with a given conditions (other predicates). +func HasCartsWith(preds ...predicate.Cart) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := newCartsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User(sql.AndPredicates(predicates...)) diff --git a/ent/user_create.go b/ent/user_create.go index 56761fd..deeccce 100644 --- a/ent/user_create.go +++ b/ent/user_create.go @@ -10,6 +10,7 @@ import ( "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" "github.com/omkar273/codegeeky/ent/user" ) @@ -128,6 +129,21 @@ func (uc *UserCreate) SetNillableID(s *string) *UserCreate { return uc } +// AddCartIDs adds the "carts" edge to the Cart entity by IDs. +func (uc *UserCreate) AddCartIDs(ids ...string) *UserCreate { + uc.mutation.AddCartIDs(ids...) + return uc +} + +// AddCarts adds the "carts" edges to the Cart entity. +func (uc *UserCreate) AddCarts(c ...*Cart) *UserCreate { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return uc.AddCartIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uc *UserCreate) Mutation() *UserMutation { return uc.mutation @@ -290,6 +306,22 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { _spec.SetField(user.FieldRole, field.TypeString, value) _node.Role = value } + if nodes := uc.mutation.CartsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.CartsTable, + Columns: []string{user.CartsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/ent/user_query.go b/ent/user_query.go index 0c96583..1f39162 100644 --- a/ent/user_query.go +++ b/ent/user_query.go @@ -4,6 +4,7 @@ package ent import ( "context" + "database/sql/driver" "fmt" "math" @@ -11,6 +12,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" "github.com/omkar273/codegeeky/ent/predicate" "github.com/omkar273/codegeeky/ent/user" ) @@ -22,6 +24,7 @@ type UserQuery struct { order []user.OrderOption inters []Interceptor predicates []predicate.User + withCarts *CartQuery // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -58,6 +61,28 @@ func (uq *UserQuery) Order(o ...user.OrderOption) *UserQuery { return uq } +// QueryCarts chains the current query on the "carts" edge. +func (uq *UserQuery) QueryCarts() *CartQuery { + query := (&CartClient{config: uq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := uq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := uq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, selector), + sqlgraph.To(cart.Table, cart.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.CartsTable, user.CartsColumn), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first User entity from the query. // Returns a *NotFoundError when no User was found. func (uq *UserQuery) First(ctx context.Context) (*User, error) { @@ -250,12 +275,24 @@ func (uq *UserQuery) Clone() *UserQuery { order: append([]user.OrderOption{}, uq.order...), inters: append([]Interceptor{}, uq.inters...), predicates: append([]predicate.User{}, uq.predicates...), + withCarts: uq.withCarts.Clone(), // clone intermediate query. sql: uq.sql.Clone(), path: uq.path, } } +// WithCarts tells the query-builder to eager-load the nodes that are connected to +// the "carts" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithCarts(opts ...func(*CartQuery)) *UserQuery { + query := (&CartClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + uq.withCarts = query + return uq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -332,8 +369,11 @@ func (uq *UserQuery) prepareQuery(ctx context.Context) error { func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, error) { var ( - nodes = []*User{} - _spec = uq.querySpec() + nodes = []*User{} + _spec = uq.querySpec() + loadedTypes = [1]bool{ + uq.withCarts != nil, + } ) _spec.ScanValues = func(columns []string) ([]any, error) { return (*User).scanValues(nil, columns) @@ -341,6 +381,7 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e _spec.Assign = func(columns []string, values []any) error { node := &User{config: uq.config} nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } for i := range hooks { @@ -352,9 +393,47 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e if len(nodes) == 0 { return nodes, nil } + if query := uq.withCarts; query != nil { + if err := uq.loadCarts(ctx, query, nodes, + func(n *User) { n.Edges.Carts = []*Cart{} }, + func(n *User, e *Cart) { n.Edges.Carts = append(n.Edges.Carts, e) }); err != nil { + return nil, err + } + } return nodes, nil } +func (uq *UserQuery) loadCarts(ctx context.Context, query *CartQuery, nodes []*User, init func(*User), assign func(*User, *Cart)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(cart.FieldUserID) + } + query.Where(predicate.Cart(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(user.CartsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.UserID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "user_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} + func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) { _spec := uq.querySpec() _spec.Node.Columns = uq.ctx.Fields diff --git a/ent/user_update.go b/ent/user_update.go index 9710c05..da5887b 100644 --- a/ent/user_update.go +++ b/ent/user_update.go @@ -11,6 +11,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/omkar273/codegeeky/ent/cart" "github.com/omkar273/codegeeky/ent/predicate" "github.com/omkar273/codegeeky/ent/user" ) @@ -124,11 +125,47 @@ func (uu *UserUpdate) SetNillableRole(s *string) *UserUpdate { return uu } +// AddCartIDs adds the "carts" edge to the Cart entity by IDs. +func (uu *UserUpdate) AddCartIDs(ids ...string) *UserUpdate { + uu.mutation.AddCartIDs(ids...) + return uu +} + +// AddCarts adds the "carts" edges to the Cart entity. +func (uu *UserUpdate) AddCarts(c ...*Cart) *UserUpdate { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return uu.AddCartIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uu *UserUpdate) Mutation() *UserMutation { return uu.mutation } +// ClearCarts clears all "carts" edges to the Cart entity. +func (uu *UserUpdate) ClearCarts() *UserUpdate { + uu.mutation.ClearCarts() + return uu +} + +// RemoveCartIDs removes the "carts" edge to Cart entities by IDs. +func (uu *UserUpdate) RemoveCartIDs(ids ...string) *UserUpdate { + uu.mutation.RemoveCartIDs(ids...) + return uu +} + +// RemoveCarts removes "carts" edges to Cart entities. +func (uu *UserUpdate) RemoveCarts(c ...*Cart) *UserUpdate { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return uu.RemoveCartIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { uu.defaults() @@ -224,6 +261,51 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { if value, ok := uu.mutation.Role(); ok { _spec.SetField(user.FieldRole, field.TypeString, value) } + if uu.mutation.CartsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.CartsTable, + Columns: []string{user.CartsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedCartsIDs(); len(nodes) > 0 && !uu.mutation.CartsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.CartsTable, + Columns: []string{user.CartsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.CartsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.CartsTable, + Columns: []string{user.CartsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{user.Label} @@ -340,11 +422,47 @@ func (uuo *UserUpdateOne) SetNillableRole(s *string) *UserUpdateOne { return uuo } +// AddCartIDs adds the "carts" edge to the Cart entity by IDs. +func (uuo *UserUpdateOne) AddCartIDs(ids ...string) *UserUpdateOne { + uuo.mutation.AddCartIDs(ids...) + return uuo +} + +// AddCarts adds the "carts" edges to the Cart entity. +func (uuo *UserUpdateOne) AddCarts(c ...*Cart) *UserUpdateOne { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return uuo.AddCartIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uuo *UserUpdateOne) Mutation() *UserMutation { return uuo.mutation } +// ClearCarts clears all "carts" edges to the Cart entity. +func (uuo *UserUpdateOne) ClearCarts() *UserUpdateOne { + uuo.mutation.ClearCarts() + return uuo +} + +// RemoveCartIDs removes the "carts" edge to Cart entities by IDs. +func (uuo *UserUpdateOne) RemoveCartIDs(ids ...string) *UserUpdateOne { + uuo.mutation.RemoveCartIDs(ids...) + return uuo +} + +// RemoveCarts removes "carts" edges to Cart entities. +func (uuo *UserUpdateOne) RemoveCarts(c ...*Cart) *UserUpdateOne { + ids := make([]string, len(c)) + for i := range c { + ids[i] = c[i].ID + } + return uuo.RemoveCartIDs(ids...) +} + // Where appends a list predicates to the UserUpdate builder. func (uuo *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne { uuo.mutation.Where(ps...) @@ -470,6 +588,51 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) if value, ok := uuo.mutation.Role(); ok { _spec.SetField(user.FieldRole, field.TypeString, value) } + if uuo.mutation.CartsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.CartsTable, + Columns: []string{user.CartsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedCartsIDs(); len(nodes) > 0 && !uuo.mutation.CartsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.CartsTable, + Columns: []string{user.CartsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.CartsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.CartsTable, + Columns: []string{user.CartsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(cart.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &User{config: uuo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/go.mod b/go.mod index c9a9767..938cee5 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/samber/lo v1.47.0 github.com/shopspring/decimal v1.4.0 github.com/spf13/viper v1.20.1 + github.com/stretchr/testify v1.10.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.8.12 diff --git a/go.sum b/go.sum index 1b3ed99..248670b 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,6 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -166,8 +164,6 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= @@ -183,8 +179,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/razorpay/razorpay-go v1.3.4 h1:A9DZ18GZDn/bGRjQ9SesTGUNIAEw+IB27512l3I81aI= github.com/razorpay/razorpay-go v1.3.4/go.mod h1:VcljkUylUJAUEvFfGVv/d5ht1to1dUgF4H1+3nv7i+Q= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -204,8 +198,6 @@ github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= diff --git a/internal/api/dto/internship.go b/internal/api/dto/internship.go index 0605a26..2ae25d9 100644 --- a/internal/api/dto/internship.go +++ b/internal/api/dto/internship.go @@ -30,59 +30,92 @@ type CreateInternshipRequest struct { CategoryIDs []string `json:"category_ids,omitempty"` } -func (i *CreateInternshipRequest) Validate() error { - err := validator.ValidateRequest(i) +func (req *CreateInternshipRequest) Validate() error { + err := validator.ValidateRequest(req) if err != nil { return ierr.WithError(err). WithHint("invalid internship create request"). Mark(ierr.ErrValidation) } - if i.FlatDiscount != nil && i.PercentageDiscount != nil { + if req.FlatDiscount != nil && req.PercentageDiscount != nil { return ierr.NewError("both flat discount and percentage discount cannot be provided"). WithHint("please provide only one of flat discount or percentage discount"). Mark(ierr.ErrValidation) } // validate level - if err := validator.ValidateEnums([]types.InternshipLevel{i.Level}, types.InternshipLevels, "level"); err != nil { + if err := req.Level.Validate(); err != nil { return ierr.WithError(err). WithHint("invalid level"). Mark(ierr.ErrValidation) } // validate mode - if err := validator.ValidateEnums([]types.InternshipMode{i.Mode}, types.InternshipModes, "mode"); err != nil { + if err := req.Mode.Validate(); err != nil { return ierr.WithError(err). WithHint("invalid mode"). Mark(ierr.ErrValidation) } + if req.Price.LessThan(decimal.Zero) { + return ierr.NewError("price cannot be less than zero"). + WithHint("please provide a valid price"). + Mark(ierr.ErrValidation) + } + + if req.FlatDiscount != nil && req.FlatDiscount.LessThan(decimal.Zero) { + return ierr.NewError("flat discount cannot be less than zero"). + WithHint("please provide a valid flat discount"). + Mark(ierr.ErrValidation) + } + + if req.PercentageDiscount != nil && req.PercentageDiscount.LessThan(decimal.Zero) { + return ierr.NewError("percentage discount cannot be less than zero"). + WithHint("please provide a valid percentage discount"). + Mark(ierr.ErrValidation) + } + return nil } -func (i *CreateInternshipRequest) ToInternship(ctx context.Context) *domainInternship.Internship { +func (req *CreateInternshipRequest) ToInternship(ctx context.Context) *domainInternship.Internship { + total := req.Price + if req.FlatDiscount != nil { + total = total.Sub(lo.FromPtr(req.FlatDiscount)) + } + if req.PercentageDiscount != nil { + discountAmount := total.Mul(lo.FromPtr(req.PercentageDiscount)).Div(decimal.NewFromInt(100)) + total = total.Sub(discountAmount) + } + + if total.LessThan(decimal.Zero) { + total = decimal.Zero + } + return &domainInternship.Internship{ ID: types.GenerateUUIDWithPrefix(types.UUID_PREFIX_INTERNSHIP), - Title: i.Title, - Description: i.Description, - LookupKey: i.LookupKey, - Skills: i.Skills, - Level: i.Level, - Mode: i.Mode, - DurationInWeeks: i.DurationInWeeks, - LearningOutcomes: i.LearningOutcomes, - Prerequisites: i.Prerequisites, - Benefits: i.Benefits, - Currency: i.Currency, - Price: i.Price, - FlatDiscount: lo.FromPtr(i.FlatDiscount), - PercentageDiscount: lo.FromPtr(i.PercentageDiscount), - Categories: lo.Map(i.CategoryIDs, func(id string, _ int) *domainInternship.Category { + Title: req.Title, + Description: req.Description, + LookupKey: req.LookupKey, + Skills: req.Skills, + Level: req.Level, + Mode: req.Mode, + DurationInWeeks: req.DurationInWeeks, + LearningOutcomes: req.LearningOutcomes, + Prerequisites: req.Prerequisites, + Benefits: req.Benefits, + Currency: req.Currency, + Price: req.Price, + FlatDiscount: req.FlatDiscount, + PercentageDiscount: req.PercentageDiscount, + Categories: lo.Map(req.CategoryIDs, func(id string, _ int) *domainInternship.Category { return &domainInternship.Category{ ID: id, } }), + Subtotal: req.Price, + Total: total, BaseModel: types.GetDefaultBaseModel(ctx), } } diff --git a/internal/api/dto/internship_batch.go b/internal/api/dto/internship_batch.go new file mode 100644 index 0000000..d0a969d --- /dev/null +++ b/internal/api/dto/internship_batch.go @@ -0,0 +1,115 @@ +package dto + +import ( + "context" + "time" + + domainInternship "github.com/omkar273/codegeeky/internal/domain/internship" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/omkar273/codegeeky/internal/validator" + "github.com/samber/lo" +) + +// CreateInternshipBatchRequest is used for creating a new internship batch via API request +type CreateInternshipBatchRequest struct { + InternshipID string `json:"internship_id" binding:"required"` + Name string `json:"name" binding:"required,min=3,max=255"` + Description string `json:"description,omitempty"` + StartDate *time.Time `json:"start_date,omitempty"` + EndDate *time.Time `json:"end_date,omitempty"` + BatchStatus types.InternshipBatchStatus `json:"batch_status,omitempty"` +} + +func (b *CreateInternshipBatchRequest) Validate() error { + err := validator.ValidateRequest(b) + if err != nil { + return ierr.WithError(err). + WithHint("invalid internship batch create request"). + Mark(ierr.ErrValidation) + } + + // validate batch status if provided + if b.BatchStatus != "" { + if err := validator.ValidateEnums([]types.InternshipBatchStatus{b.BatchStatus}, types.InternshipBatchStatuses, "batch_status"); err != nil { + return ierr.WithError(err). + WithHint("invalid batch status"). + Mark(ierr.ErrValidation) + } + } + + // validate dates if both are provided + if b.StartDate != nil && b.EndDate != nil && b.StartDate.After(*b.EndDate) { + return ierr.NewError("start date must be before end date"). + WithHint("please provide a valid date range"). + Mark(ierr.ErrValidation) + } + + return nil +} + +func (b *CreateInternshipBatchRequest) ToInternshipBatch(ctx context.Context) *domainInternship.InternshipBatch { + batchStatus := b.BatchStatus + if batchStatus == "" { + batchStatus = types.InternshipBatchStatusUpcoming + } + + return &domainInternship.InternshipBatch{ + ID: types.GenerateUUIDWithPrefix(types.UUID_PREFIX_INTERNSHIP_BATCH), + InternshipID: b.InternshipID, + Name: b.Name, + Description: b.Description, + StartDate: lo.FromPtr(b.StartDate), + EndDate: lo.FromPtr(b.EndDate), + BatchStatus: batchStatus, + BaseModel: types.GetDefaultBaseModel(ctx), + } +} + +type InternshipBatchResponse struct { + domainInternship.InternshipBatch +} + +func (b *InternshipBatchResponse) FromDomain(batch *domainInternship.InternshipBatch) *InternshipBatchResponse { + return &InternshipBatchResponse{ + InternshipBatch: *batch, + } +} + +// UpdateInternshipBatchRequest is used for updating an existing internship batch via API request +type UpdateInternshipBatchRequest struct { + Name *string `json:"name,omitempty" binding:"omitempty,min=3,max=255"` + Description *string `json:"description,omitempty"` + StartDate *time.Time `json:"start_date,omitempty"` + EndDate *time.Time `json:"end_date,omitempty"` + BatchStatus *types.InternshipBatchStatus `json:"batch_status,omitempty"` +} + +func (b *UpdateInternshipBatchRequest) Validate() error { + err := validator.ValidateRequest(b) + if err != nil { + return ierr.WithError(err). + WithHint("invalid internship batch update request"). + Mark(ierr.ErrValidation) + } + + // validate batch status if provided + if b.BatchStatus != nil { + if err := validator.ValidateEnums([]types.InternshipBatchStatus{*b.BatchStatus}, types.InternshipBatchStatuses, "batch_status"); err != nil { + return ierr.WithError(err). + WithHint("invalid batch status"). + Mark(ierr.ErrValidation) + } + } + + // validate dates if both are provided + if b.StartDate != nil && b.EndDate != nil && b.StartDate.After(*b.EndDate) { + return ierr.NewError("start date must be before end date"). + WithHint("please provide a valid date range"). + Mark(ierr.ErrValidation) + } + + return nil +} + +type ListInternshipBatchResponse = types.ListResponse[*InternshipBatchResponse] diff --git a/internal/api/dto/internship_enrollment.go b/internal/api/dto/internship_enrollment.go new file mode 100644 index 0000000..317c6a5 --- /dev/null +++ b/internal/api/dto/internship_enrollment.go @@ -0,0 +1,29 @@ +package dto + +import ( + "github.com/omkar273/codegeeky/internal/types" + "github.com/omkar273/codegeeky/internal/validator" +) + +// InitializeEnrollmentRequest is the request for initializing an internship enrollment +type InitializeEnrollmentRequest struct { + InternshipBatchID string `json:"internship_batch_id" validate:"required"` + CouponCodes []string `json:"coupon_codes,omitempty"` + SuccessURL string `json:"success_url" validate:"required,url"` + CancelURL string `json:"cancel_url" validate:"required,url"` + Metadata map[string]string `json:"metadata,omitempty" validate:"omitempty"` +} + +func (r *InitializeEnrollmentRequest) Validate() error { + if err := validator.ValidateRequest(r); err != nil { + return err + } + + return nil +} + +type InitializeEnrollmentResponse struct { + EnrollmentID string `json:"enrollment_id"` + EnrollmentStatus types.InternshipEnrollmentStatus `json:"enrollment_status"` + PaymentRequired bool `json:"payment_required"` +} diff --git a/internal/api/dto/payment.go b/internal/api/dto/payment.go index 516d14f..7a6c477 100644 --- a/internal/api/dto/payment.go +++ b/internal/api/dto/payment.go @@ -1,21 +1,263 @@ package dto +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/domain/payment" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/idempotency" + "github.com/omkar273/codegeeky/internal/types" + "github.com/shopspring/decimal" +) + +// PaymentRequest represents a request to create a payment type PaymentRequest struct { - Amount int64 - Currency string - CustomerID string - ReturnURL string - Metadata map[string]string - PaymentMode string // e.g. "card", "upi", "netbanking" + // reference id + // ID of the resource (internship ID, subscription ID, etc.) + ReferenceID string `json:"reference_id"` + ReferenceType types.PaymentDestinationType `json:"reference_type"` + + // destination id + // ID of the destination (internship ID, subscription ID, etc.) + DestinationID string `json:"destination_id"` + DestinationType types.PaymentDestinationType `json:"destination_type"` + + // amount + // Amount in smallest unit (e.g. paisa) + Amount int64 `json:"amount"` + + // currency + // "INR", "USD", etc. + Currency string `json:"currency"` + + // payment gateway provider + // razorpay, stripe, etc. + PaymentGatewayProvider types.PaymentGatewayProvider `json:"payment_gateway_provider"` + + // payment method type + // upi, card, wallet, etc. + PaymentMethodType *types.PaymentMethodType `json:"payment_method_type,omitempty"` + PaymentMethodID *string `json:"payment_method_id,omitempty"` + + // success url + // Frontend success redirect + SuccessURL string `json:"success_url,omitempty"` + + // cancel url + // Frontend cancel redirect + CancelURL string `json:"cancel_url,omitempty"` + + // return url + // For legacy gateway naming compatibility + ReturnURL string `json:"return_url,omitempty"` + + // idempotency key + // Optional, for safe retries + IdempotencyKey string `json:"idempotency_key,omitempty"` + + // metadata + // Additional tracking info (origin, cohort, etc.) + Metadata map[string]string `json:"metadata,omitempty"` + + // track attempts + // Whether to track payment attempts + TrackAttempts bool `json:"track_attempts,omitempty"` } +// Validate validates the payment request +func (r *PaymentRequest) Validate() error { + if r.Amount <= 0 { + return ierr.NewError("invalid amount"). + WithHint("Amount must be greater than 0"). + Mark(ierr.ErrValidation) + } + + if r.DestinationID == "" { + return ierr.NewError("invalid destination id"). + WithHint("Destination id is required"). + Mark(ierr.ErrValidation) + } + + if err := r.DestinationType.Validate(); err != nil { + return ierr.NewError("invalid destination type"). + WithHint("Destination type is invalid"). + Mark(ierr.ErrValidation) + } + + if r.Currency == "" { + return ierr.NewError("invalid currency"). + WithHint("Currency is required"). + Mark(ierr.ErrValidation) + } + + if err := r.PaymentGatewayProvider.Validate(); err != nil { + return ierr.NewError("invalid payment gateway provider"). + WithHint("Payment gateway provider is invalid"). + Mark(ierr.ErrValidation) + } + + if r.PaymentMethodType != nil && r.PaymentMethodType.Validate() != nil { + return ierr.NewError("invalid payment method type"). + WithHint("Payment method type is invalid"). + Mark(ierr.ErrValidation) + } + + return nil +} + +// ToPayment converts the request to a domain payment model +func (r *PaymentRequest) ToPayment(ctx context.Context) *payment.Payment { + now := time.Now() + userID := types.GetUserID(ctx) + + // Generate idempotency key if not provided + idempotencyKey := r.IdempotencyKey + if idempotencyKey == "" { + generator := idempotency.NewGenerator() + params := map[string]interface{}{ + "destination_id": r.DestinationID, + "destination_type": r.DestinationType, + "amount": r.Amount, + "currency": r.Currency, + "user_id": userID, + "timestamp": now.Unix(), + } + idempotencyKey = generator.GenerateKey(idempotency.ScopePayment, params) + } + + // Convert amount from int64 to decimal + // TODO: This is a temporary solution to convert the amount to decimal. + amount := decimal.NewFromInt(r.Amount).Div(decimal.NewFromInt(100)) + + paymentMethodID := "" + if r.PaymentMethodID != nil { + paymentMethodID = *r.PaymentMethodID + } + + return &payment.Payment{ + ID: types.GenerateUUIDWithPrefix(types.UUID_PREFIX_PAYMENT), + IdempotencyKey: idempotencyKey, + DestinationType: r.DestinationType, + DestinationID: r.DestinationID, + PaymentMethodType: r.PaymentMethodType, + PaymentMethodID: paymentMethodID, + PaymentGatewayProvider: r.PaymentGatewayProvider, + Amount: amount, + Currency: types.Currency(r.Currency), + PaymentStatus: types.PaymentStatusPending, + TrackAttempts: r.TrackAttempts, + Metadata: r.Metadata, + BaseModel: types.BaseModel{ + Status: types.StatusPublished, + CreatedAt: now, + UpdatedAt: now, + CreatedBy: userID, + UpdatedBy: userID, + }, + } +} + +// CreatePaymentRequest represents a request to create a payment +type CreatePaymentRequest struct { + PaymentRequest +} + +// UpdatePaymentRequest represents a request to update a payment +type UpdatePaymentRequest struct { + PaymentStatus *types.PaymentStatus `json:"payment_status,omitempty"` + GatewayPaymentID *string `json:"gateway_payment_id,omitempty"` + ErrorMessage *string `json:"error_message,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +// Validate validates the update request +func (r *UpdatePaymentRequest) Validate() error { + if r.PaymentStatus != nil && r.PaymentStatus.Validate() != nil { + return ierr.NewError("invalid payment status"). + WithHint("Payment status is invalid"). + Mark(ierr.ErrValidation) + } + return nil +} + +// PaymentResponse represents a payment response type PaymentResponse struct { - ProviderPaymentID string - RedirectURL string - Status string - Raw map[string]interface{} // Raw provider response + Payment payment.Payment `json:"payment"` + + // Gateway specific response data + GatewayResponse *PaymentGatewayResponse `json:"gateway_response,omitempty"` +} + +// PaymentGatewayResponse represents the response from payment gateway +type PaymentGatewayResponse struct { + ProviderPaymentID string `json:"provider_payment_id"` + RedirectURL string `json:"redirect_url,omitempty"` + Status string `json:"status"` + Raw map[string]interface{} `json:"raw,omitempty"` // Raw provider response +} + +// ListPaymentResponse represents a paginated list of payments +type ListPaymentResponse struct { + Items []*PaymentResponse `json:"items"` + Pagination *types.PaginationResponse `json:"pagination"` +} + +// PaymentAttemptRequest represents a request to create a payment attempt +type PaymentAttemptRequest struct { + PaymentID string `json:"payment_id"` + PaymentStatus types.PaymentStatus `json:"payment_status"` + GatewayAttemptID *string `json:"gateway_attempt_id,omitempty"` + ErrorMessage *string `json:"error_message,omitempty"` + Metadata types.Metadata `json:"metadata,omitempty"` +} + +// Validate validates the payment attempt request +func (r *PaymentAttemptRequest) Validate() error { + if r.PaymentID == "" { + return ierr.NewError("invalid payment id"). + WithHint("Payment id is required"). + Mark(ierr.ErrValidation) + } + + if err := r.PaymentStatus.Validate(); err != nil { + return ierr.NewError("invalid payment status"). + WithHint("Payment status is invalid"). + Mark(ierr.ErrValidation) + } + + return nil +} + +// ToPaymentAttempt converts the request to a domain payment attempt model +func (r *PaymentAttemptRequest) ToPaymentAttempt(ctx context.Context, attemptNumber int) *payment.PaymentAttempt { + now := time.Now() + userID := types.GetUserID(ctx) + + return &payment.PaymentAttempt{ + ID: types.GenerateUUIDWithPrefix(types.UUID_PREFIX_PAYMENT_ATTEMPT), + PaymentID: r.PaymentID, + AttemptNumber: attemptNumber, + PaymentStatus: r.PaymentStatus, + GatewayAttemptID: r.GatewayAttemptID, + ErrorMessage: r.ErrorMessage, + Metadata: r.Metadata, + BaseModel: types.BaseModel{ + Status: types.StatusPublished, + CreatedAt: now, + UpdatedAt: now, + CreatedBy: userID, + UpdatedBy: userID, + }, + } +} + +// PaymentAttemptResponse represents a payment attempt response +type PaymentAttemptResponse struct { + Attempt payment.PaymentAttempt `json:"attempt"` } +// PaymentStatus represents the current status of a payment type PaymentStatus struct { Status string Reason string @@ -23,6 +265,7 @@ type PaymentStatus struct { Raw map[string]interface{} } +// WebhookResult represents the result of processing a webhook type WebhookResult struct { EventName string EventID string diff --git a/internal/api/dto/pricing.go b/internal/api/dto/pricing.go new file mode 100644 index 0000000..717d96b --- /dev/null +++ b/internal/api/dto/pricing.go @@ -0,0 +1,37 @@ +package dto + +import ( + "github.com/shopspring/decimal" +) + +// PricingRequest represents a request to calculate pricing for an internship +type PricingRequest struct { + InternshipID string `json:"internship_id" validate:"required"` + DiscountCode string `json:"discount_code,omitempty"` +} + +// PricingResponse provides pricing information for enrollment and coupon validation +type PricingResponse struct { + InternshipID string `json:"internship_id"` + + // Pricing breakdown + Subtotal decimal.Decimal `json:"subtotal"` + DiscountAmount decimal.Decimal `json:"discount_amount"` + Total decimal.Decimal `json:"total"` + Currency string `json:"currency"` + + // Discount information (if any discount is applied) + AppliedDiscounts []*DiscountInfo `json:"applied_discount,omitempty"` + + // User-friendly information + PaymentRequired bool `json:"payment_required"` + SavingsPercent float64 `json:"savings_percent,omitempty"` +} + +// DiscountInfo contains information about applied discounts +type DiscountInfo struct { + Code string `json:"code,omitempty"` // coupon code if applicable + Amount decimal.Decimal `json:"amount"` // discount amount + Description string `json:"description"` // user-friendly description + IsValid bool `json:"is_valid"` // for coupon validation +} diff --git a/internal/api/router.go b/internal/api/router.go index 5268706..a9fb9de 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -84,6 +84,5 @@ func NewRouter(handlers *Handlers, cfg *config.Configuration, logger *logger.Log v1Discount.PUT("/:id", handlers.Discount.UpdateDiscount) v1Discount.DELETE("/:id", handlers.Discount.DeleteDiscount) } - return router } diff --git a/internal/domain/cart/line_items.go b/internal/domain/cart/line_items.go new file mode 100644 index 0000000..0811db7 --- /dev/null +++ b/internal/domain/cart/line_items.go @@ -0,0 +1,53 @@ +package cart + +import ( + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" + "github.com/shopspring/decimal" +) + +// CartLineItems is the model entity for the CartLineItems schema. +type CartLineItem struct { + ID string `json:"id,omitempty"` + CartID string `json:"cart_id,omitempty"` + EntityID string `json:"entity_id,omitempty"` + EntityType types.CartLineItemEntityType `json:"entity_type,omitempty"` + Quantity int `json:"quantity,omitempty"` + PerUnitPrice decimal.Decimal `json:"per_unit_price,omitempty"` + TaxAmount decimal.Decimal `json:"tax_amount,omitempty"` + DiscountAmount decimal.Decimal `json:"discount_amount,omitempty"` + Subtotal decimal.Decimal `json:"subtotal,omitempty"` + Total decimal.Decimal `json:"total,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` + types.BaseModel +} + +func (c *CartLineItem) FromEnt(ent *ent.CartLineItems) *CartLineItem { + return &CartLineItem{ + ID: ent.ID, + CartID: ent.CartID, + EntityID: ent.EntityID, + EntityType: types.CartLineItemEntityType(ent.EntityType), + Quantity: ent.Quantity, + PerUnitPrice: ent.PerUnitPrice, + TaxAmount: ent.TaxAmount, + DiscountAmount: ent.DiscountAmount, + Subtotal: ent.Subtotal, + Total: ent.Total, + Metadata: ent.Metadata, + BaseModel: types.BaseModel{ + CreatedAt: ent.CreatedAt, + UpdatedAt: ent.UpdatedAt, + CreatedBy: ent.CreatedBy, + UpdatedBy: ent.UpdatedBy, + Status: types.Status(ent.Status), + }, + } +} + +func (c *CartLineItem) FromEntList(ents []*ent.CartLineItems) []*CartLineItem { + return lo.Map(ents, func(ent *ent.CartLineItems, _ int) *CartLineItem { + return c.FromEnt(ent) + }) +} diff --git a/internal/domain/cart/model.go b/internal/domain/cart/model.go new file mode 100644 index 0000000..75b70af --- /dev/null +++ b/internal/domain/cart/model.go @@ -0,0 +1,53 @@ +package cart + +import ( + "time" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" + "github.com/shopspring/decimal" +) + +type Cart struct { + ID string `json:"id,omitempty"` + UserID string `json:"user_id,omitempty"` + Type types.CartType `json:"type,omitempty"` + Subtotal decimal.Decimal `json:"subtotal,omitempty"` + DiscountAmount decimal.Decimal `json:"discount_amount,omitempty"` + TaxAmount decimal.Decimal `json:"tax_amount,omitempty"` + Total decimal.Decimal `json:"total,omitempty"` + ExpiresAt time.Time `json:"expires_at,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` + LineItems []*CartLineItem `json:"line_items,omitempty"` + types.BaseModel +} + +func (c *Cart) FromEnt(ent *ent.Cart) *Cart { + li := &CartLineItem{} + return &Cart{ + ID: ent.ID, + UserID: ent.UserID, + Type: types.CartType(ent.Type), + Subtotal: ent.Subtotal, + DiscountAmount: ent.DiscountAmount, + TaxAmount: ent.TaxAmount, + Total: ent.Total, + ExpiresAt: ent.ExpiresAt, + Metadata: ent.Metadata, + LineItems: li.FromEntList(ent.Edges.LineItems), + BaseModel: types.BaseModel{ + CreatedAt: ent.CreatedAt, + UpdatedAt: ent.UpdatedAt, + CreatedBy: ent.CreatedBy, + UpdatedBy: ent.UpdatedBy, + Status: types.Status(ent.Status), + }, + } +} + +func (c *Cart) FromEntList(ents []*ent.Cart) []*Cart { + return lo.Map(ents, func(ent *ent.Cart, _ int) *Cart { + return c.FromEnt(ent) + }) +} diff --git a/internal/domain/cart/repository.go b/internal/domain/cart/repository.go new file mode 100644 index 0000000..40cdd6e --- /dev/null +++ b/internal/domain/cart/repository.go @@ -0,0 +1,25 @@ +package cart + +import ( + "context" + + "github.com/omkar273/codegeeky/internal/types" +) + +type Repository interface { + Create(ctx context.Context, cart *Cart) error + CreateWithLineItems(ctx context.Context, cart *Cart) error + Get(ctx context.Context, id string) (*Cart, error) + Update(ctx context.Context, cart *Cart) error + Delete(ctx context.Context, id string) error + Count(ctx context.Context, filter *types.CartFilter) (int, error) + List(ctx context.Context, filter *types.CartFilter) ([]*Cart, error) + ListAll(ctx context.Context, filter *types.CartFilter) ([]*Cart, error) + + // cart line items + CreateCartLineItem(ctx context.Context, cartLineItem *CartLineItem) error + GetCartLineItem(ctx context.Context, id string) (*CartLineItem, error) + UpdateCartLineItem(ctx context.Context, cartLineItem *CartLineItem) error + DeleteCartLineItem(ctx context.Context, id string) error + ListCartLineItems(ctx context.Context, cartId string) ([]*CartLineItem, error) +} diff --git a/internal/domain/discount/model.go b/internal/domain/discount/model.go index d2b5b9f..2dca4cb 100644 --- a/internal/domain/discount/model.go +++ b/internal/domain/discount/model.go @@ -5,6 +5,7 @@ import ( "github.com/omkar273/codegeeky/ent" "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" "github.com/shopspring/decimal" ) @@ -48,10 +49,8 @@ func FromEnt(ent *ent.Discount) *Discount { } } -func FromEntList(ents []*ent.Discount) []*Discount { - discounts := make([]*Discount, len(ents)) - for i, ent := range ents { - discounts[i] = FromEnt(ent) - } - return discounts +func (d *Discount) FromEntList(ents []*ent.Discount) []*Discount { + return lo.Map(ents, func(ent *ent.Discount, _ int) *Discount { + return FromEnt(ent) + }) } diff --git a/internal/domain/internship/batch.go b/internal/domain/internship/batch.go new file mode 100644 index 0000000..7278c92 --- /dev/null +++ b/internal/domain/internship/batch.go @@ -0,0 +1,48 @@ +package internship + +import ( + "time" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// InternshipBatch is the model entity for the InternshipBatch schema. +type InternshipBatch struct { + ID string `json:"id,omitempty"` + InternshipID string `json:"internship_id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + StartDate time.Time `json:"start_date,omitempty"` + EndDate time.Time `json:"end_date,omitempty"` + BatchStatus types.InternshipBatchStatus `json:"batch_status,omitempty"` + types.Metadata `json:"metadata,omitempty"` + types.BaseModel +} + +func (b *InternshipBatch) FromEnt(ent *ent.InternshipBatch) *InternshipBatch { + return &InternshipBatch{ + ID: ent.ID, + InternshipID: ent.InternshipID, + Name: ent.Name, + Description: ent.Description, + StartDate: ent.StartDate, + EndDate: ent.EndDate, + BatchStatus: types.InternshipBatchStatus(ent.BatchStatus), + Metadata: types.MetadataFromEnt(ent.Metadata), + BaseModel: types.BaseModel{ + Status: types.Status(ent.Status), + CreatedAt: ent.CreatedAt, + UpdatedAt: ent.UpdatedAt, + CreatedBy: ent.CreatedBy, + UpdatedBy: ent.UpdatedBy, + }, + } +} + +func (b *InternshipBatch) FromEntList(ents []*ent.InternshipBatch) []*InternshipBatch { + return lo.Map(ents, func(ent *ent.InternshipBatch, _ int) *InternshipBatch { + return b.FromEnt(ent) + }) +} diff --git a/internal/domain/internship/category.go b/internal/domain/internship/category.go new file mode 100644 index 0000000..6791565 --- /dev/null +++ b/internal/domain/internship/category.go @@ -0,0 +1,51 @@ +package internship + +import ( + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +type Category struct { + // ID of the ent. + ID string `json:"id,omitempty" db:"id"` + + // Name holds the value of the "name" field. + Name string `json:"name,omitempty" db:"name"` + + // LookupKey holds the value of the "lookup_key" field. + LookupKey string `json:"lookup_key,omitempty" db:"lookup_key"` + + // Description holds the value of the "description" field. + Description string `json:"description,omitempty" db:"description"` + + // internships holds the value of the internships edge. + Internships []*Internship `json:"internships,omitempty" db:"internships"` + + types.BaseModel +} + +func (c *Category) FromEnt(category *ent.Category) *Category { + internship := &Internship{} + + return &Category{ + ID: category.ID, + Name: category.Name, + LookupKey: category.LookupKey, + Description: category.Description, + Internships: internship.FromEntList(category.Edges.Internships), + BaseModel: types.BaseModel{ + Status: types.Status(category.Status), + CreatedAt: category.CreatedAt, + UpdatedAt: category.UpdatedAt, + CreatedBy: category.CreatedBy, + UpdatedBy: category.UpdatedBy, + }, + } +} + +func (c *Category) FromEntList(categories []*ent.Category) []*Category { + return lo.Map(categories, func(category *ent.Category, _ int) *Category { + return c.FromEnt(category) + }) +} diff --git a/internal/domain/internship/model.go b/internal/domain/internship/model.go index 7e79264..333dbf5 100644 --- a/internal/domain/internship/model.go +++ b/internal/domain/internship/model.go @@ -8,65 +8,39 @@ import ( ) type Internship struct { - - // ID of the ent. - ID string `json:"id,omitempty" db:"id"` - - // Title holds the value of the "title" field. - Title string `json:"title,omitempty" db:"title"` - - // Description holds the value of the "description" field. - Description string `json:"description,omitempty" db:"description"` - - // LookupKey holds the value of the "lookup_key" field. - LookupKey string `json:"lookup_key,omitempty" db:"lookup_key"` - - // List of required skills - Skills []string `json:"skills,omitempty" db:"skills"` - - // Level of the internship: beginner, intermediate, advanced - Level types.InternshipLevel `json:"level,omitempty" db:"level"` - - // Internship mode: remote, hybrid, onsite - Mode types.InternshipMode `json:"mode,omitempty" db:"mode"` - - // Alternative to months for shorter internships - DurationInWeeks int `json:"duration_in_weeks,omitempty" db:"duration_in_weeks"` - - // What students will learn in the internship - LearningOutcomes []string `json:"learning_outcomes,omitempty" db:"learning_outcomes"` - - // Prerequisites or recommended knowledge - Prerequisites []string `json:"prerequisites,omitempty" db:"prerequisites"` - - // Benefits of the internship - Benefits []string `json:"benefits,omitempty" db:"benefits"` - - // Currency of the internship - Currency string `json:"currency,omitempty" db:"currency"` - - // Price of the internship - Price decimal.Decimal `json:"price,omitempty" db:"price"` - - // Flat discount on the internship - FlatDiscount decimal.Decimal `json:"flat_discount,omitempty" db:"flat_discount"` - - // Percentage discount on the internship - PercentageDiscount decimal.Decimal `json:"percentage_discount,omitempty" db:"percentage_discount"` - - // Categories holds the value of the categories edge. - Categories []*Category `json:"categories,omitempty" db:"categories"` + ID string `json:"id,omitempty"` + Title string `json:"title,omitempty"` + LookupKey string `json:"lookup_key,omitempty"` + Description string `json:"description,omitempty"` + Skills []string `json:"skills,omitempty"` + Level types.InternshipLevel `json:"level,omitempty"` + Mode types.InternshipMode `json:"mode,omitempty"` + DurationInWeeks int `json:"duration_in_weeks,omitempty"` + LearningOutcomes []string `json:"learning_outcomes,omitempty"` + Prerequisites []string `json:"prerequisites,omitempty"` + Benefits []string `json:"benefits,omitempty"` + Currency string `json:"currency,omitempty"` + Price decimal.Decimal `json:"price,omitempty"` + FlatDiscount *decimal.Decimal `json:"flat_discount,omitempty"` + PercentageDiscount *decimal.Decimal `json:"percentage_discount,omitempty"` + Subtotal decimal.Decimal `json:"subtotal,omitempty"` + Total decimal.Decimal `json:"total,omitempty"` + Categories []*Category `json:"categories,omitempty" db:"categories"` types.BaseModel } -func InternshipFromEnt(internship *ent.Internship) *Internship { +func (i *Internship) FromEnt(internship *ent.Internship) *Internship { + c := &Category{} + return &Internship{ ID: internship.ID, Title: internship.Title, Description: internship.Description, LookupKey: internship.LookupKey, Skills: internship.Skills, + Subtotal: internship.Subtotal, + Total: internship.Total, Level: types.InternshipLevel(internship.Level), Mode: types.InternshipMode(internship.Mode), DurationInWeeks: internship.DurationInWeeks, @@ -77,7 +51,7 @@ func InternshipFromEnt(internship *ent.Internship) *Internship { Price: internship.Price, FlatDiscount: internship.FlatDiscount, PercentageDiscount: internship.PercentageDiscount, - Categories: CategoryFromEntList(internship.Edges.Categories), + Categories: c.FromEntList(internship.Edges.Categories), BaseModel: types.BaseModel{ Status: types.Status(internship.Status), CreatedAt: internship.CreatedAt, @@ -88,62 +62,8 @@ func InternshipFromEnt(internship *ent.Internship) *Internship { } } -func InternshipFromEntList(internships []*ent.Internship) []*Internship { +func (i *Internship) FromEntList(internships []*ent.Internship) []*Internship { return lo.Map(internships, func(internship *ent.Internship, _ int) *Internship { - return InternshipFromEnt(internship) + return i.FromEnt(internship) }) } - -type Category struct { - // ID of the ent. - ID string `json:"id,omitempty" db:"id"` - - // Name holds the value of the "name" field. - Name string `json:"name,omitempty" db:"name"` - - // LookupKey holds the value of the "lookup_key" field. - LookupKey string `json:"lookup_key,omitempty" db:"lookup_key"` - - // Description holds the value of the "description" field. - Description string `json:"description,omitempty" db:"description"` - - // Internships holds the value of the internships edge. - Internships []*Internship `json:"internships,omitempty" db:"internships"` - - types.BaseModel -} - -func CategoryFromEnt(category *ent.Category) *Category { - return &Category{ - ID: category.ID, - Name: category.Name, - LookupKey: category.LookupKey, - Description: category.Description, - Internships: InternshipFromEntList(category.Edges.Internships), - BaseModel: types.BaseModel{ - Status: types.Status(category.Status), - CreatedAt: category.CreatedAt, - UpdatedAt: category.UpdatedAt, - CreatedBy: category.CreatedBy, - UpdatedBy: category.UpdatedBy, - }, - } -} - -func CategoryFromEntList(categories []*ent.Category) []*Category { - return lo.Map(categories, func(category *ent.Category, _ int) *Category { - return CategoryFromEnt(category) - }) -} - -func (i *Internship) FinalPrice() decimal.Decimal { - price := i.Price - if !i.FlatDiscount.IsZero() && !i.FlatDiscount.IsNegative() { - price = price.Sub(i.FlatDiscount) - } - if !i.PercentageDiscount.IsZero() && !i.PercentageDiscount.IsNegative() { - discount := price.Mul(i.PercentageDiscount.Div(decimal.NewFromInt(100))) - price = price.Sub(discount) - } - return price -} diff --git a/internal/domain/internship/repository.go b/internal/domain/internship/repository.go index eb70dab..e0cbd98 100644 --- a/internal/domain/internship/repository.go +++ b/internal/domain/internship/repository.go @@ -27,3 +27,13 @@ type CategoryRepository interface { List(ctx context.Context, filter *types.CategoryFilter) ([]*Category, error) ListAll(ctx context.Context, filter *types.CategoryFilter) ([]*Category, error) } + +type InternshipBatchRepository interface { + Create(ctx context.Context, batch *InternshipBatch) error + Get(ctx context.Context, id string) (*InternshipBatch, error) + Update(ctx context.Context, batch *InternshipBatch) error + Delete(ctx context.Context, id string) error + Count(ctx context.Context, filter *types.InternshipBatchFilter) (int, error) + List(ctx context.Context, filter *types.InternshipBatchFilter) ([]*InternshipBatch, error) + ListAll(ctx context.Context, filter *types.InternshipBatchFilter) ([]*InternshipBatch, error) +} diff --git a/internal/domain/internshipenrollment/model.go b/internal/domain/internshipenrollment/model.go new file mode 100644 index 0000000..c7b3711 --- /dev/null +++ b/internal/domain/internshipenrollment/model.go @@ -0,0 +1,55 @@ +package internshipenrollment + +import ( + "time" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/internal/types" +) + +// InternshipEnrollment is the model entity for the InternshipEnrollment schema. +type InternshipEnrollment struct { + ID string `json:"id,omitempty"` + UserID string `json:"user_id,omitempty"` + InternshipID string `json:"internship_id,omitempty"` + EnrollmentStatus types.InternshipEnrollmentStatus `json:"enrollment_status,omitempty"` + PaymentStatus types.PaymentStatus `json:"payment_status,omitempty"` + EnrolledAt *time.Time `json:"enrolled_at,omitempty"` + PaymentID *string `json:"payment_id,omitempty"` + RefundedAt *time.Time `json:"refunded_at,omitempty"` + CancellationReason *string `json:"cancellation_reason,omitempty"` + RefundReason *string `json:"refund_reason,omitempty"` + IdempotencyKey *string `json:"idempotency_key,omitempty"` + types.Metadata `json:"metadata,omitempty"` + types.BaseModel +} + +func FromEnt(ent *ent.InternshipEnrollment) *InternshipEnrollment { + return &InternshipEnrollment{ + ID: ent.ID, + UserID: ent.UserID, + InternshipID: ent.InternshipID, + EnrollmentStatus: ent.EnrollmentStatus, + PaymentStatus: ent.PaymentStatus, + EnrolledAt: ent.EnrolledAt, + PaymentID: ent.PaymentID, + RefundedAt: ent.RefundedAt, + CancellationReason: ent.CancellationReason, + RefundReason: ent.RefundReason, + Metadata: types.MetadataFromEnt(ent.Metadata), + BaseModel: types.BaseModel{ + CreatedAt: ent.CreatedAt, + UpdatedAt: ent.UpdatedAt, + CreatedBy: ent.CreatedBy, + UpdatedBy: ent.UpdatedBy, + }, + } +} + +func FromEntList(ents []*ent.InternshipEnrollment) []*InternshipEnrollment { + enrollments := make([]*InternshipEnrollment, len(ents)) + for i, ent := range ents { + enrollments[i] = FromEnt(ent) + } + return enrollments +} diff --git a/internal/domain/internshipenrollment/repository.go b/internal/domain/internshipenrollment/repository.go new file mode 100644 index 0000000..e683ff9 --- /dev/null +++ b/internal/domain/internshipenrollment/repository.go @@ -0,0 +1,18 @@ +package internshipenrollment + +import ( + "context" + + "github.com/omkar273/codegeeky/internal/types" +) + +type Repository interface { + Create(ctx context.Context, enrollment *InternshipEnrollment) error + Get(ctx context.Context, id string) (*InternshipEnrollment, error) + Update(ctx context.Context, enrollment *InternshipEnrollment) error + Delete(ctx context.Context, id string) error + Count(ctx context.Context, filter *types.InternshipEnrollmentFilter) (int, error) + List(ctx context.Context, filter *types.InternshipEnrollmentFilter) ([]*InternshipEnrollment, error) + ListAll(ctx context.Context, filter *types.InternshipEnrollmentFilter) ([]*InternshipEnrollment, error) + GetByIdempotencyKey(ctx context.Context, idempotencyKey string) (*InternshipEnrollment, error) +} diff --git a/internal/domain/payment/model.go b/internal/domain/payment/model.go index bcd1508..0ce8bbc 100644 --- a/internal/domain/payment/model.go +++ b/internal/domain/payment/model.go @@ -12,38 +12,51 @@ import ( // Payment represents a payment transaction type Payment struct { - ID string `json:"id"` - IdempotencyKey string `json:"idempotency_key"` - - // payment destination - DestinationType types.PaymentDestinationType `json:"destination_type"` - DestinationID string `json:"destination_id"` - PaymentMethodType types.PaymentMethodType `json:"payment_method_type"` - PaymentMethodID string `json:"payment_method_id"` - - // payment gateway - PaymentGatewayProvider types.PaymentGatewayProvider `json:"payment_gateway_provider"` - GatewayPaymentID *string `json:"gateway_payment_id,omitempty"` - - // payment amount and currency - Amount decimal.Decimal `json:"amount"` - Currency types.Currency `json:"currency"` - - // payment status - PaymentStatus types.PaymentStatus `json:"payment_status"` - - // payment tracking - TrackAttempts bool `json:"track_attempts"` - SucceededAt *time.Time `json:"succeeded_at,omitempty"` - FailedAt *time.Time `json:"failed_at,omitempty"` - RefundedAt *time.Time `json:"refunded_at,omitempty"` - - // payment error - ErrorMessage *string `json:"error_message,omitempty"` - - // payment metadata - Metadata types.Metadata `json:"metadata,omitempty"` - Attempts []*PaymentAttempt `json:"attempts,omitempty"` + // ID of the ent. + ID string `json:"id,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // UpdatedBy holds the value of the "updated_by" field. + UpdatedBy string `json:"updated_by,omitempty"` + // IdempotencyKey holds the value of the "idempotency_key" field. + IdempotencyKey string `json:"idempotency_key,omitempty"` + // DestinationType holds the value of the "destination_type" field. + DestinationType types.PaymentDestinationType `json:"destination_type,omitempty"` + // DestinationID holds the value of the "destination_id" field. + DestinationID string `json:"destination_id,omitempty"` + // PaymentMethodType holds the value of the "payment_method_type" field. + PaymentMethodType *types.PaymentMethodType `json:"payment_method_type,omitempty"` + // PaymentMethodID holds the value of the "payment_method_id" field. + PaymentMethodID string `json:"payment_method_id,omitempty"` + // PaymentGatewayProvider holds the value of the "payment_gateway_provider" field. + PaymentGatewayProvider types.PaymentGatewayProvider `json:"payment_gateway_provider,omitempty"` + // GatewayPaymentID holds the value of the "gateway_payment_id" field. + GatewayPaymentID *string `json:"gateway_payment_id,omitempty"` + // Amount holds the value of the "amount" field. + Amount decimal.Decimal `json:"amount,omitempty"` + // Currency holds the value of the "currency" field. + Currency types.Currency `json:"currency,omitempty"` + // PaymentStatus holds the value of the "payment_status" field. + PaymentStatus types.PaymentStatus `json:"payment_status,omitempty"` + // TrackAttempts holds the value of the "track_attempts" field. + TrackAttempts bool `json:"track_attempts,omitempty"` + // Metadata holds the value of the "metadata" field. + Metadata map[string]string `json:"metadata,omitempty"` + // SucceededAt holds the value of the "succeeded_at" field. + SucceededAt *time.Time `json:"succeeded_at,omitempty"` + // FailedAt holds the value of the "failed_at" field. + FailedAt *time.Time `json:"failed_at,omitempty"` + // RefundedAt holds the value of the "refunded_at" field. + RefundedAt *time.Time `json:"refunded_at,omitempty"` + // ErrorMessage holds the value of the "error_message" field. + ErrorMessage *string `json:"error_message,omitempty"` + Attempts []*PaymentAttempt `json:"attempts,omitempty"` types.BaseModel } @@ -61,6 +74,7 @@ type PaymentAttempt struct { // Validate validates the payment func (p *Payment) Validate() error { + if p.Amount.IsZero() || p.Amount.IsNegative() { return ierr.NewError("invalid amount"). WithHint("Amount must be greater than 0"). @@ -79,7 +93,7 @@ func (p *Payment) Validate() error { Mark(ierr.ErrValidation) } - if err := p.PaymentMethodType.Validate(); err != nil { + if p.PaymentMethodType != nil && p.PaymentMethodType.Validate() != nil { return ierr.NewError("invalid payment method type"). WithHint("Payment method type is invalid"). Mark(ierr.ErrValidation) @@ -91,13 +105,13 @@ func (p *Payment) Validate() error { Mark(ierr.ErrValidation) } - if p.PaymentMethodType == types.PaymentMethodTypeOffline && p.PaymentMethodID != "" { + if p.PaymentMethodType != nil && lo.FromPtr(p.PaymentMethodType) == types.PaymentMethodTypeOffline && p.PaymentMethodID != "" { return ierr.NewError("payment method id is not allowed for offline payment method type"). WithHint("Payment method id is invalid"). Mark(ierr.ErrValidation) } - if p.PaymentMethodType != types.PaymentMethodTypeOffline && p.PaymentMethodID == "" { + if p.PaymentMethodType != nil && lo.FromPtr(p.PaymentMethodType) != types.PaymentMethodTypeOffline && p.PaymentMethodID == "" { return ierr.NewError("invalid payment method id"). WithHint("Payment method id is required"). Mark(ierr.ErrValidation) @@ -146,7 +160,7 @@ func FromEnt(p *ent.Payment) *Payment { DestinationID: p.DestinationID, PaymentMethodType: p.PaymentMethodType, PaymentMethodID: p.PaymentMethodID, - PaymentGatewayProvider: lo.FromPtr(p.PaymentGatewayProvider), + PaymentGatewayProvider: p.PaymentGatewayProvider, GatewayPaymentID: p.GatewayPaymentID, Amount: p.Amount, Currency: p.Currency, diff --git a/internal/payment/factory.go b/internal/payment/factory.go index 831b5f8..7543fbc 100644 --- a/internal/payment/factory.go +++ b/internal/payment/factory.go @@ -2,9 +2,12 @@ package gateway import ( "context" - "fmt" "sync" + "github.com/omkar273/codegeeky/internal/config" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/httpclient" + razorpay "github.com/omkar273/codegeeky/internal/payment/providers" "github.com/omkar273/codegeeky/internal/types" ) @@ -21,6 +24,18 @@ func NewGatewayRegistryService() GatewayRegistryService { } } +func InitializeProviders(ctx context.Context, client *httpclient.Client, config *config.Configuration) GatewayRegistryService { + registry := NewGatewayRegistryService() + + // razorpay + registry.RegisterProvider( + types.PaymentGatewayProviderRazorpay, + razorpay.NewRazorpayProvider(client, config), + ) + + return registry +} + // RegisterProvider registers an instantiated provider under a name func (r *gatewayRegistryService) RegisterProvider(name types.PaymentGatewayProvider, provider GatewayProvider) { r.mu.Lock() @@ -28,14 +43,25 @@ func (r *gatewayRegistryService) RegisterProvider(name types.PaymentGatewayProvi r.providers[name] = provider } -// GetProvider returns a provider based on selection attributes (e.g., selected payment provider) -func (r *gatewayRegistryService) GetProvider(ctx context.Context, attrs *types.SelectionAttributes) (GatewayProvider, error) { +// GetProviderByName returns a provider based on name (e.g., selected payment provider) +func (r *gatewayRegistryService) GetProviderByName(ctx context.Context, name types.PaymentGatewayProvider) (GatewayProvider, error) { + if name == "" { + return nil, ierr.NewError("provider name is required"). + WithHint("Please provide a valid payment gateway provider name"). + Mark(ierr.ErrValidation) + } + r.mu.RLock() defer r.mu.RUnlock() - provider, ok := r.providers[attrs.PaymentGateway] + provider, ok := r.providers[name] if !ok { - return nil, fmt.Errorf("provider %s not found", attrs.PaymentGateway) + return nil, ierr.NewError("provider not found"). + WithHint("Please provide a valid payment gateway provider name"). + WithReportableDetails(map[string]any{ + "name": name, + }). + Mark(ierr.ErrValidation) } return provider, nil diff --git a/internal/payment/gateway.go b/internal/payment/gateway.go index ec80f66..1a00f6d 100644 --- a/internal/payment/gateway.go +++ b/internal/payment/gateway.go @@ -21,15 +21,15 @@ type GatewayProvider interface { // ProcessWebhook is a generic handler for incoming webhooks from the provider ProcessWebhook(ctx context.Context, payload []byte, headers map[string]string) (*dto.WebhookResult, error) - // CreatePaymentRequest is a generic abstraction to initiate payments - CreatePaymentRequest(ctx context.Context, input *dto.PaymentRequest) (*dto.PaymentResponse, error) + // CreatePaymentOrder is a generic abstraction to initiate payments + CreatePaymentOrder(ctx context.Context, input *dto.PaymentRequest) (*dto.PaymentResponse, error) // VerifyPaymentStatus checks status of a payment from provider VerifyPaymentStatus(ctx context.Context, providerPaymentID string) (*dto.PaymentStatus, error) } type GatewayRegistryService interface { - GetProvider(ctx context.Context, attrs *types.SelectionAttributes) (GatewayProvider, error) + GetProviderByName(ctx context.Context, name types.PaymentGatewayProvider) (GatewayProvider, error) ListAvailableProviders(ctx context.Context) ([]types.PaymentGatewayProvider, error) RegisterProvider(name types.PaymentGatewayProvider, provider GatewayProvider) } diff --git a/internal/payment/providers/razorpay.go b/internal/payment/providers/razorpay.go index 566c366..565410f 100644 --- a/internal/payment/providers/razorpay.go +++ b/internal/payment/providers/razorpay.go @@ -7,6 +7,7 @@ import ( "github.com/omkar273/codegeeky/internal/api/dto" "github.com/omkar273/codegeeky/internal/config" + "github.com/omkar273/codegeeky/internal/domain/payment" "github.com/omkar273/codegeeky/internal/httpclient" "github.com/omkar273/codegeeky/internal/types" "github.com/razorpay/razorpay-go" @@ -63,7 +64,7 @@ func (r *RazorpayProvider) ProcessWebhook(ctx context.Context, payload []byte, h }, nil } -func (r *RazorpayProvider) CreatePaymentRequest(ctx context.Context, input *dto.PaymentRequest) (*dto.PaymentResponse, error) { +func (r *RazorpayProvider) CreatePaymentOrder(ctx context.Context, input *dto.PaymentRequest) (*dto.PaymentResponse, error) { // create order order, err := r.razorpayClient.Order.Create(map[string]interface{}{ "amount": input.Amount, @@ -83,10 +84,9 @@ func (r *RazorpayProvider) CreatePaymentRequest(ctx context.Context, input *dto. // return payment details return &dto.PaymentResponse{ - ProviderPaymentID: order["id"].(string), - RedirectURL: order["short_url"].(string), - Status: "success", - Raw: map[string]any{"order_id": order["id"].(string)}, + Payment: payment.Payment{ + ID: order["id"].(string), + }, }, nil } diff --git a/internal/repository/ent/cart.go b/internal/repository/ent/cart.go new file mode 100644 index 0000000..b96e743 --- /dev/null +++ b/internal/repository/ent/cart.go @@ -0,0 +1,642 @@ +package ent + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/ent/cart" + "github.com/omkar273/codegeeky/ent/cartlineitems" + domainCart "github.com/omkar273/codegeeky/internal/domain/cart" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/postgres" + "github.com/omkar273/codegeeky/internal/types" +) + +type cartRepository struct { + client postgres.IClient + logger logger.Logger + queryOpts CartQueryOptions +} + +func NewCartRepository(client postgres.IClient, logger *logger.Logger) domainCart.Repository { + return &cartRepository{ + client: client, + logger: *logger, + queryOpts: CartQueryOptions{}, + } +} + +func (r *cartRepository) Create(ctx context.Context, cartData *domainCart.Cart) error { + client := r.client.Querier(ctx) + + r.logger.Debugw("creating cart", + "cart_id", cartData.ID, + "user_id", cartData.UserID, + "type", cartData.Type, + ) + + _, err := client.Cart.Create(). + SetID(cartData.ID). + SetUserID(cartData.UserID). + SetType(string(cartData.Type)). + SetSubtotal(cartData.Subtotal). + SetDiscountAmount(cartData.DiscountAmount). + SetTaxAmount(cartData.TaxAmount). + SetTotal(cartData.Total). + SetExpiresAt(cartData.ExpiresAt). + SetMetadata(cartData.Metadata). + SetStatus(string(types.StatusPublished)). + SetCreatedAt(cartData.CreatedAt). + SetUpdatedAt(cartData.UpdatedAt). + SetCreatedBy(cartData.CreatedBy). + SetUpdatedBy(cartData.UpdatedBy). + Save(ctx) + + if err != nil { + if ent.IsConstraintError(err) { + return ierr.WithError(err). + WithHint("Cart with this ID already exists"). + WithReportableDetails(map[string]any{ + "cart_id": cartData.ID, + "user_id": cartData.UserID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create cart"). + WithReportableDetails(map[string]any{ + "cart_id": cartData.ID, + "user_id": cartData.UserID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *cartRepository) CreateWithLineItems(ctx context.Context, cartData *domainCart.Cart) error { + client := r.client.Querier(ctx) + + r.logger.Debugw("creating cart with line items", + "cart_id", cartData.ID, + "line_items_count", len(cartData.LineItems), + ) + + return r.client.WithTx(ctx, func(ctx context.Context) error { + // 1. Create cart + _, err := client.Cart.Create(). + SetID(cartData.ID). + SetUserID(cartData.UserID). + SetType(string(cartData.Type)). + SetSubtotal(cartData.Subtotal). + SetDiscountAmount(cartData.DiscountAmount). + SetTaxAmount(cartData.TaxAmount). + SetTotal(cartData.Total). + SetExpiresAt(cartData.ExpiresAt). + SetMetadata(cartData.Metadata). + SetStatus(string(types.StatusPublished)). + SetCreatedAt(cartData.CreatedAt). + SetUpdatedAt(cartData.UpdatedAt). + SetCreatedBy(cartData.CreatedBy). + SetUpdatedBy(cartData.UpdatedBy). + Save(ctx) + + if err != nil { + if ent.IsConstraintError(err) { + return ierr.WithError(err). + WithHint("Cart with this ID already exists"). + WithReportableDetails(map[string]any{ + "cart_id": cartData.ID, + "user_id": cartData.UserID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create cart"). + WithReportableDetails(map[string]any{ + "cart_id": cartData.ID, + "user_id": cartData.UserID, + }). + Mark(ierr.ErrDatabase) + } + + // 2. Create line items in bulk if present + if len(cartData.LineItems) > 0 { + builders := make([]*ent.CartLineItemsCreate, len(cartData.LineItems)) + for i, item := range cartData.LineItems { + builders[i] = client.CartLineItems.Create(). + SetID(item.ID). + SetCartID(cartData.ID). + SetEntityID(item.EntityID). + SetEntityType(string(item.EntityType)). + SetQuantity(item.Quantity). + SetPerUnitPrice(item.PerUnitPrice). + SetTaxAmount(item.TaxAmount). + SetDiscountAmount(item.DiscountAmount). + SetSubtotal(item.Subtotal). + SetTotal(item.Total). + SetMetadata(item.Metadata). + SetStatus(string(types.StatusPublished)). + SetCreatedBy(item.CreatedBy). + SetUpdatedBy(item.UpdatedBy). + SetCreatedAt(item.CreatedAt). + SetUpdatedAt(item.UpdatedAt) + } + + if err := client.CartLineItems.CreateBulk(builders...).Exec(ctx); err != nil { + r.logger.Error("failed to create line items", "error", err) + return ierr.WithError(err).WithHint("line item creation failed").Mark(ierr.ErrDatabase) + } + } + + return nil + }) +} + +func (r *cartRepository) Get(ctx context.Context, id string) (*domainCart.Cart, error) { + client := r.client.Querier(ctx) + + r.logger.Debugw("getting cart", "cart_id", id) + + entCart, err := client.Cart.Query(). + Where(cart.ID(id)). + WithLineItems(). + Only(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHintf("Cart with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "cart_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHint("Failed to get cart"). + WithReportableDetails(map[string]any{ + "cart_id": id, + }). + Mark(ierr.ErrDatabase) + } + + cartData := &domainCart.Cart{} + return cartData.FromEnt(entCart), nil +} + +func (r *cartRepository) Update(ctx context.Context, cartData *domainCart.Cart) error { + client := r.client.Querier(ctx) + + r.logger.Debugw("updating cart", + "cart_id", cartData.ID, + "user_id", cartData.UserID, + ) + + _, err := client.Cart.UpdateOneID(cartData.ID). + SetMetadata(cartData.Metadata). + SetStatus(string(cartData.Status)). + SetUpdatedAt(time.Now().UTC()). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return ierr.WithError(err). + WithHintf("Cart with ID %s was not found", cartData.ID). + WithReportableDetails(map[string]any{ + "cart_id": cartData.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHint("Failed to update cart"). + WithReportableDetails(map[string]any{ + "cart_id": cartData.ID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *cartRepository) Delete(ctx context.Context, id string) error { + client := r.client.Querier(ctx) + + r.logger.Debugw("deleting cart", "cart_id", id) + + return r.client.WithTx(ctx, func(ctx context.Context) error { + // Delete line items first + _, err := client.CartLineItems.Update(). + Where(cartlineitems.CartID(id)). + SetStatus(string(types.StatusDeleted)). + SetUpdatedBy(types.GetUserID(ctx)). + SetUpdatedAt(time.Now().UTC()). + Save(ctx) + if err != nil { + return ierr.WithError(err).WithHint("line item deletion failed").Mark(ierr.ErrDatabase) + } + + // Then delete cart + _, err = client.Cart.UpdateOneID(id). + SetStatus(string(types.StatusDeleted)). + SetUpdatedAt(time.Now().UTC()). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return ierr.WithError(err). + WithHintf("Cart with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "cart_id": id, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHint("Failed to delete cart"). + WithReportableDetails(map[string]any{ + "cart_id": id, + }). + Mark(ierr.ErrDatabase) + } + + return nil + }) +} + +func (r *cartRepository) Count(ctx context.Context, filter *types.CartFilter) (int, error) { + client := r.client.Querier(ctx) + + r.logger.Debugw("counting carts") + + query := client.Cart.Query() + query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) + query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) + + count, err := query.Count(ctx) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count carts"). + Mark(ierr.ErrDatabase) + } + + return count, nil +} + +func (r *cartRepository) List(ctx context.Context, filter *types.CartFilter) ([]*domainCart.Cart, error) { + client := r.client.Querier(ctx) + + r.logger.Debugw("listing carts", + "limit", filter.GetLimit(), + "offset", filter.GetOffset(), + ) + + query := client.Cart.Query() + query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) + query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) + + carts, err := query. + WithLineItems(). + All(ctx) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list carts"). + Mark(ierr.ErrDatabase) + } + + cartData := &domainCart.Cart{} + return cartData.FromEntList(carts), nil +} + +func (r *cartRepository) ListAll(ctx context.Context, filter *types.CartFilter) ([]*domainCart.Cart, error) { + if filter == nil { + filter = types.NewNoLimitCartFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + carts, err := r.List(ctx, filter) + if err != nil { + return nil, err + } + return carts, nil +} + +// Cart line items methods +func (r *cartRepository) CreateCartLineItem(ctx context.Context, cartLineItem *domainCart.CartLineItem) error { + client := r.client.Querier(ctx) + + r.logger.Debugw("creating cart line item", + "line_item_id", cartLineItem.ID, + "cart_id", cartLineItem.CartID, + ) + + _, err := client.CartLineItems.Create(). + SetID(cartLineItem.ID). + SetCartID(cartLineItem.CartID). + SetEntityID(cartLineItem.EntityID). + SetEntityType(string(cartLineItem.EntityType)). + SetQuantity(cartLineItem.Quantity). + SetPerUnitPrice(cartLineItem.PerUnitPrice). + SetTaxAmount(cartLineItem.TaxAmount). + SetDiscountAmount(cartLineItem.DiscountAmount). + SetSubtotal(cartLineItem.Subtotal). + SetTotal(cartLineItem.Total). + SetMetadata(cartLineItem.Metadata). + SetStatus(string(types.StatusPublished)). + SetCreatedAt(cartLineItem.CreatedAt). + SetUpdatedAt(cartLineItem.UpdatedAt). + SetCreatedBy(cartLineItem.CreatedBy). + SetUpdatedBy(cartLineItem.UpdatedBy). + Save(ctx) + + if err != nil { + if ent.IsConstraintError(err) { + return ierr.WithError(err). + WithHint("Cart line item with this ID already exists"). + WithReportableDetails(map[string]any{ + "line_item_id": cartLineItem.ID, + "cart_id": cartLineItem.CartID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create cart line item"). + WithReportableDetails(map[string]any{ + "line_item_id": cartLineItem.ID, + "cart_id": cartLineItem.CartID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *cartRepository) GetCartLineItem(ctx context.Context, id string) (*domainCart.CartLineItem, error) { + client := r.client.Querier(ctx) + + r.logger.Debugw("getting cart line item", "line_item_id", id) + + entCartLineItem, err := client.CartLineItems.Query(). + Where(cartlineitems.ID(id)). + Only(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHintf("Cart line item with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "line_item_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHint("Failed to get cart line item"). + WithReportableDetails(map[string]any{ + "line_item_id": id, + }). + Mark(ierr.ErrDatabase) + } + + cartLineItem := &domainCart.CartLineItem{} + return cartLineItem.FromEnt(entCartLineItem), nil +} + +func (r *cartRepository) UpdateCartLineItem(ctx context.Context, cartLineItem *domainCart.CartLineItem) error { + client := r.client.Querier(ctx) + + r.logger.Debugw("updating cart line item", + "line_item_id", cartLineItem.ID, + "cart_id", cartLineItem.CartID, + ) + + _, err := client.CartLineItems.UpdateOneID(cartLineItem.ID). + SetQuantity(cartLineItem.Quantity). + SetPerUnitPrice(cartLineItem.PerUnitPrice). + SetTaxAmount(cartLineItem.TaxAmount). + SetDiscountAmount(cartLineItem.DiscountAmount). + SetSubtotal(cartLineItem.Subtotal). + SetTotal(cartLineItem.Total). + SetMetadata(cartLineItem.Metadata). + SetStatus(string(cartLineItem.Status)). + SetUpdatedAt(time.Now().UTC()). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return ierr.WithError(err). + WithHintf("Cart line item with ID %s was not found", cartLineItem.ID). + WithReportableDetails(map[string]any{ + "line_item_id": cartLineItem.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHint("Failed to update cart line item"). + WithReportableDetails(map[string]any{ + "line_item_id": cartLineItem.ID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *cartRepository) DeleteCartLineItem(ctx context.Context, id string) error { + client := r.client.Querier(ctx) + + r.logger.Debugw("deleting cart line item", "line_item_id", id) + + _, err := client.CartLineItems.UpdateOneID(id). + SetStatus(string(types.StatusDeleted)). + SetUpdatedAt(time.Now().UTC()). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return ierr.WithError(err). + WithHintf("Cart line item with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "line_item_id": id, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHint("Failed to delete cart line item"). + WithReportableDetails(map[string]any{ + "line_item_id": id, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *cartRepository) ListCartLineItems(ctx context.Context, cartId string) ([]*domainCart.CartLineItem, error) { + client := r.client.Querier(ctx) + + r.logger.Debugw("listing cart line items", "cart_id", cartId) + + cartLineItems, err := client.CartLineItems.Query(). + Where( + cartlineitems.CartID(cartId), + cartlineitems.Status(string(types.StatusPublished)), + ). + All(ctx) + + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list cart line items"). + WithReportableDetails(map[string]any{ + "cart_id": cartId, + }). + Mark(ierr.ErrDatabase) + } + + cartLineItem := &domainCart.CartLineItem{} + return cartLineItem.FromEntList(cartLineItems), nil +} + +// CartQuery type alias for better readability +type CartQuery = *ent.CartQuery + +// CartQueryOptions implements query options for cart queries +type CartQueryOptions struct { + QueryOptionsHelper +} + +// Ensure CartQueryOptions implements EntityQueryOptions interface +var _ EntityQueryOptions[CartQuery, *types.CartFilter] = (*CartQueryOptions)(nil) + +func (o CartQueryOptions) ApplyStatusFilter(query CartQuery, status string) CartQuery { + if status == "" { + return query.Where(cart.StatusNotIn(string(types.StatusDeleted))) + } + return query.Where(cart.Status(status)) +} + +func (o CartQueryOptions) ApplySortFilter(query CartQuery, field string, order string) CartQuery { + field, order = o.QueryOptionsHelper.ValidateSort(field, order) + fieldName := o.GetFieldName(field) + if order == types.OrderDesc { + return query.Order(ent.Desc(fieldName)) + } + return query.Order(ent.Asc(fieldName)) +} + +func (o CartQueryOptions) ApplyPaginationFilter(query CartQuery, limit int, offset int) CartQuery { + limit, offset = o.QueryOptionsHelper.ValidatePagination(limit, offset) + return query.Offset(offset).Limit(limit) +} + +func (o CartQueryOptions) GetFieldName(field string) string { + switch field { + case "created_at": + return cart.FieldCreatedAt + case "updated_at": + return cart.FieldUpdatedAt + case "user_id": + return cart.FieldUserID + case "type": + return cart.FieldType + case "subtotal": + return cart.FieldSubtotal + case "discount_amount": + return cart.FieldDiscountAmount + case "tax_amount": + return cart.FieldTaxAmount + case "total": + return cart.FieldTotal + case "expires_at": + return cart.FieldExpiresAt + case "metadata": + return cart.FieldMetadata + case "created_by": + return cart.FieldCreatedBy + case "updated_by": + return cart.FieldUpdatedBy + default: + return "" + } +} + +func (o CartQueryOptions) ApplyBaseFilters( + _ context.Context, + query CartQuery, + filter *types.CartFilter, +) CartQuery { + if filter == nil { + return query.Where(cart.StatusNotIn(string(types.StatusDeleted))) + } + + // Apply status filter + query = o.ApplyStatusFilter(query, filter.GetStatus()) + + // Apply pagination + if !filter.IsUnlimited() { + query = o.ApplyPaginationFilter(query, filter.GetLimit(), filter.GetOffset()) + } + + // Apply sorting + query = o.ApplySortFilter(query, filter.GetSort(), filter.GetOrder()) + + return query +} + +func (o CartQueryOptions) ApplyEntityQueryOptions( + _ context.Context, + f *types.CartFilter, + query CartQuery, +) CartQuery { + if f == nil { + return query + } + + // Apply user ID filter if specified + if f.UserID != "" { + query = query.Where(cart.UserID(f.UserID)) + } + + // Apply cart type filter if specified + if f.CartType != nil { + query = query.Where(cart.Type(string(*f.CartType))) + } + + // Apply expires at filter if specified + if f.ExpiresAt != nil { + query = query.Where(cart.ExpiresAtGTE(*f.ExpiresAt)) + } + + // Apply entity filters if specified + if f.EntityID != "" { + query = query.Where(cart.HasLineItemsWith(cartlineitems.EntityID(f.EntityID))) + } + + if f.EntityType != "" { + query = query.Where(cart.HasLineItemsWith(cartlineitems.EntityType(string(f.EntityType)))) + } + + // Apply time range filters if specified + if f.TimeRangeFilter != nil { + if f.StartTime != nil { + query = query.Where(cart.CreatedAtGTE(*f.StartTime)) + } + if f.EndTime != nil { + query = query.Where(cart.CreatedAtLTE(*f.EndTime)) + } + } + + // Apply expansion if requested + expand := f.GetExpand() + if expand.Has(types.ExpandCartLineItems) { + query = query.WithLineItems() + } + + return query +} diff --git a/internal/repository/ent/category.go b/internal/repository/ent/category.go index 5b5be3d..3aadace 100644 --- a/internal/repository/ent/category.go +++ b/internal/repository/ent/category.go @@ -97,7 +97,8 @@ func (r *categoryRepository) Get(ctx context.Context, id string) (*domainCategor Mark(ierr.ErrDatabase) } - return domainCategory.CategoryFromEnt(entCategory), nil + category := &domainCategory.Category{} + return category.FromEnt(entCategory), nil } func (r *categoryRepository) GetByLookupKey(ctx context.Context, lookupKey string) (*domainCategory.Category, error) { @@ -129,7 +130,8 @@ func (r *categoryRepository) GetByLookupKey(ctx context.Context, lookupKey strin Mark(ierr.ErrDatabase) } - return domainCategory.CategoryFromEnt(entCategory), nil + category := &domainCategory.Category{} + return category.FromEnt(entCategory), nil } func (r *categoryRepository) List(ctx context.Context, filter *types.CategoryFilter) ([]*domainCategory.Category, error) { @@ -151,7 +153,8 @@ func (r *categoryRepository) List(ctx context.Context, filter *types.CategoryFil Mark(ierr.ErrDatabase) } - return domainCategory.CategoryFromEntList(categories), nil + category := &domainCategory.Category{} + return category.FromEntList(categories), nil } func (r *categoryRepository) ListAll(ctx context.Context, filter *types.CategoryFilter) ([]*domainCategory.Category, error) { @@ -190,13 +193,14 @@ func (r *categoryRepository) Count(ctx context.Context, filter *types.CategoryFi } func (r *categoryRepository) Update(ctx context.Context, categoryData *domainCategory.Category) error { - client := r.client.Querier(ctx) r.log.Debugw("updating category", "category_id", categoryData.ID, "name", categoryData.Name, ) + client := r.client.Querier(ctx) + _, err := client.Category.UpdateOneID(categoryData.ID). SetName(categoryData.Name). SetLookupKey(categoryData.LookupKey). diff --git a/internal/repository/ent/discount.go b/internal/repository/ent/discount.go index a5a95b8..aa465b3 100644 --- a/internal/repository/ent/discount.go +++ b/internal/repository/ent/discount.go @@ -140,7 +140,8 @@ func (r *discountRepository) List(ctx context.Context, filter *types.DiscountFil Mark(ierr.ErrDatabase) } - return domainDiscount.FromEntList(discounts), nil + discount := &domainDiscount.Discount{} + return discount.FromEntList(discounts), nil } func (r *discountRepository) ListAll(ctx context.Context, filter *types.DiscountFilter) ([]*domainDiscount.Discount, error) { @@ -168,7 +169,8 @@ func (r *discountRepository) ListAll(ctx context.Context, filter *types.Discount Mark(ierr.ErrDatabase) } - return domainDiscount.FromEntList(discounts), nil + discount := &domainDiscount.Discount{} + return discount.FromEntList(discounts), nil } func (r *discountRepository) Update(ctx context.Context, discount *domainDiscount.Discount) error { @@ -343,5 +345,12 @@ func (o DiscountQueryOptions) ApplyEntityQueryOptions( query = query.Where(discount.IsCombinable(f.IsCombinable)) } + if len(f.Codes) > 0 { + query = query.Where(discount.CodeIn(f.Codes...)) + } + + if len(f.DiscountIDs) > 0 { + query = query.Where(discount.IDIn(f.DiscountIDs...)) + } return query } diff --git a/internal/repository/ent/internship.go b/internal/repository/ent/internship.go index f94eb39..71bd6bd 100644 --- a/internal/repository/ent/internship.go +++ b/internal/repository/ent/internship.go @@ -41,7 +41,7 @@ func (r *internshipRepository) Create(ctx context.Context, internshipData *domai // Convert domain categories to ent categories entCats := lo.Map(internshipData.Categories, func(c *domainInternship.Category, _ int) *ent.Category { return &ent.Category{ID: c.ID} - }) + }) // Create internship _, err := client.Internship.Create(). @@ -58,8 +58,8 @@ func (r *internshipRepository) Create(ctx context.Context, internshipData *domai SetBenefits(internshipData.Benefits). SetCurrency(internshipData.Currency). SetPrice(internshipData.Price). - SetFlatDiscount(internshipData.FlatDiscount). - SetPercentageDiscount(internshipData.PercentageDiscount). + SetFlatDiscount(lo.FromPtr(internshipData.FlatDiscount)). + SetPercentageDiscount(lo.FromPtr(internshipData.PercentageDiscount)). SetStatus(string(internshipData.Status)). SetCreatedAt(internshipData.CreatedAt). SetUpdatedAt(internshipData.UpdatedAt). @@ -117,7 +117,8 @@ func (r *internshipRepository) Get(ctx context.Context, id string) (*domainInter Mark(ierr.ErrDatabase) } - return domainInternship.InternshipFromEnt(entInternship), nil + internship := &domainInternship.Internship{} + return internship.FromEnt(entInternship), nil } func (r *internshipRepository) GetByLookupKey(ctx context.Context, lookupKey string) (*domainInternship.Internship, error) { @@ -150,7 +151,8 @@ func (r *internshipRepository) GetByLookupKey(ctx context.Context, lookupKey str Mark(ierr.ErrDatabase) } - return domainInternship.InternshipFromEnt(entInternship), nil + internship := &domainInternship.Internship{} + return internship.FromEnt(entInternship), nil } func (r *internshipRepository) List(ctx context.Context, filter *types.InternshipFilter) ([]*domainInternship.Internship, error) { @@ -165,18 +167,17 @@ func (r *internshipRepository) List(ctx context.Context, filter *types.Internshi query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) - // Add eager loading for categories - // TODO: Rethink this - // query = query.WithCategories() - - internships, err := query.All(ctx) + internships, err := query. + WithCategories(). + All(ctx) if err != nil { return nil, ierr.WithError(err). WithHint("Failed to list internships"). Mark(ierr.ErrDatabase) } - return domainInternship.InternshipFromEntList(internships), nil + internship := &domainInternship.Internship{} + return internship.FromEntList(internships), nil } func (r *internshipRepository) ListAll(ctx context.Context, filter *types.InternshipFilter) ([]*domainInternship.Internship, error) { @@ -235,8 +236,8 @@ func (r *internshipRepository) Update(ctx context.Context, internshipData *domai SetBenefits(internshipData.Benefits). SetCurrency(internshipData.Currency). SetPrice(internshipData.Price). - SetFlatDiscount(internshipData.FlatDiscount). - SetPercentageDiscount(internshipData.PercentageDiscount). + SetFlatDiscount(lo.FromPtr(internshipData.FlatDiscount)). + SetPercentageDiscount(lo.FromPtr(internshipData.PercentageDiscount)). SetStatus(string(internshipData.Status)). SetUpdatedAt(time.Now().UTC()). SetUpdatedBy(types.GetUserID(ctx)). diff --git a/internal/repository/ent/internship_batch.go b/internal/repository/ent/internship_batch.go new file mode 100644 index 0000000..a25a1aa --- /dev/null +++ b/internal/repository/ent/internship_batch.go @@ -0,0 +1,364 @@ +package ent + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/ent/internshipbatch" + domainInternship "github.com/omkar273/codegeeky/internal/domain/internship" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/postgres" + "github.com/omkar273/codegeeky/internal/types" +) + +type internshipBatchRepository struct { + client postgres.IClient + log logger.Logger + queryOpts InternshipBatchQueryOptions +} + +func NewInternshipBatchRepository(client postgres.IClient, logger *logger.Logger) domainInternship.InternshipBatchRepository { + return &internshipBatchRepository{ + client: client, + log: *logger, + queryOpts: InternshipBatchQueryOptions{}, + } +} + +func (r *internshipBatchRepository) Create(ctx context.Context, batch *domainInternship.InternshipBatch) error { + client := r.client.Querier(ctx) + + r.log.Debugw("creating internship batch", + "batch_id", batch.ID, + "internship_id", batch.InternshipID, + "name", batch.Name, + ) + + _, err := client.InternshipBatch.Create(). + SetID(batch.ID). + SetInternshipID(batch.InternshipID). + SetName(batch.Name). + SetDescription(batch.Description). + SetStartDate(batch.StartDate). + SetEndDate(batch.EndDate). + SetBatchStatus(string(batch.BatchStatus)). + SetMetadata(batch.Metadata). + SetStatus(string(types.StatusPublished)). + SetCreatedAt(batch.CreatedAt). + SetUpdatedAt(batch.UpdatedAt). + SetCreatedBy(batch.CreatedBy). + SetUpdatedBy(batch.UpdatedBy). + Save(ctx) + + if err != nil { + if ent.IsConstraintError(err) { + return ierr.WithError(err). + WithHint("Internship batch with this ID already exists"). + WithReportableDetails(map[string]any{ + "batch_id": batch.ID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create internship batch"). + WithReportableDetails(map[string]any{ + "batch_id": batch.ID, + "internship_id": batch.InternshipID, + "name": batch.Name, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *internshipBatchRepository) Get(ctx context.Context, id string) (*domainInternship.InternshipBatch, error) { + client := r.client.Querier(ctx) + + r.log.Debugw("getting internship batch", "batch_id", id) + + entBatch, err := client.InternshipBatch.Query(). + Where(internshipbatch.ID(id)). + Only(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHintf("Internship batch with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "batch_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHint("Failed to get internship batch"). + WithReportableDetails(map[string]any{ + "batch_id": id, + }). + Mark(ierr.ErrDatabase) + } + + batch := &domainInternship.InternshipBatch{} + return batch.FromEnt(entBatch), nil +} + +func (r *internshipBatchRepository) Update(ctx context.Context, batch *domainInternship.InternshipBatch) error { + client := r.client.Querier(ctx) + + r.log.Debugw("updating internship batch", + "batch_id", batch.ID, + "name", batch.Name, + ) + + _, err := client.InternshipBatch.UpdateOneID(batch.ID). + SetName(batch.Name). + SetDescription(batch.Description). + SetStartDate(batch.StartDate). + SetEndDate(batch.EndDate). + SetBatchStatus(string(batch.BatchStatus)). + SetMetadata(batch.Metadata). + SetUpdatedAt(time.Now().UTC()). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return ierr.WithError(err). + WithHintf("Internship batch with ID %s was not found", batch.ID). + WithReportableDetails(map[string]any{ + "batch_id": batch.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHint("Failed to update internship batch"). + WithReportableDetails(map[string]any{ + "batch_id": batch.ID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *internshipBatchRepository) Delete(ctx context.Context, id string) error { + client := r.client.Querier(ctx) + + r.log.Debugw("deleting internship batch", + "batch_id", id, + ) + + _, err := client.InternshipBatch.UpdateOneID(id). + SetStatus(string(types.StatusDeleted)). + SetUpdatedAt(time.Now().UTC()). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return ierr.WithError(err). + WithHintf("Internship batch with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "batch_id": id, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHint("Failed to delete internship batch"). + WithReportableDetails(map[string]any{ + "batch_id": id, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *internshipBatchRepository) Count(ctx context.Context, filter *types.InternshipBatchFilter) (int, error) { + client := r.client.Querier(ctx) + + r.log.Debugw("counting internship batches") + + query := client.InternshipBatch.Query() + query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) + query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) + + count, err := query.Count(ctx) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count internship batches"). + Mark(ierr.ErrDatabase) + } + + return count, nil +} + +func (r *internshipBatchRepository) List(ctx context.Context, filter *types.InternshipBatchFilter) ([]*domainInternship.InternshipBatch, error) { + client := r.client.Querier(ctx) + + r.log.Debugw("listing internship batches", + "limit", filter.GetLimit(), + "offset", filter.GetOffset(), + ) + + query := client.InternshipBatch.Query() + query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) + query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) + + batches, err := query.All(ctx) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list internship batches"). + Mark(ierr.ErrDatabase) + } + + batch := &domainInternship.InternshipBatch{} + return batch.FromEntList(batches), nil +} + +func (r *internshipBatchRepository) ListAll(ctx context.Context, filter *types.InternshipBatchFilter) ([]*domainInternship.InternshipBatch, error) { + if filter == nil { + filter = types.NewNoLimitInternshipBatchFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + batches, err := r.List(ctx, filter) + if err != nil { + return nil, err + } + return batches, nil +} + +// InternshipBatchQuery type alias for better readability +type InternshipBatchQuery = *ent.InternshipBatchQuery + +// InternshipBatchQueryOptions implements query options for internship batch queries +type InternshipBatchQueryOptions struct { + QueryOptionsHelper +} + +// Ensure InternshipBatchQueryOptions implements EntityQueryOptions interface +var _ EntityQueryOptions[InternshipBatchQuery, *types.InternshipBatchFilter] = (*InternshipBatchQueryOptions)(nil) + +func (o InternshipBatchQueryOptions) ApplyStatusFilter(query InternshipBatchQuery, status string) InternshipBatchQuery { + if status == "" { + return query.Where(internshipbatch.StatusNotIn(string(types.StatusDeleted))) + } + return query.Where(internshipbatch.Status(status)) +} + +func (o InternshipBatchQueryOptions) ApplySortFilter(query InternshipBatchQuery, field string, order string) InternshipBatchQuery { + field, order = o.ValidateSort(field, order) + fieldName := o.GetFieldName(field) + if order == types.OrderDesc { + return query.Order(ent.Desc(fieldName)) + } + return query.Order(ent.Asc(fieldName)) +} + +func (o InternshipBatchQueryOptions) ApplyPaginationFilter(query InternshipBatchQuery, limit int, offset int) InternshipBatchQuery { + limit, offset = o.ValidatePagination(limit, offset) + return query.Offset(offset).Limit(limit) +} + +func (o InternshipBatchQueryOptions) GetFieldName(field string) string { + switch field { + case "created_at": + return internshipbatch.FieldCreatedAt + case "updated_at": + return internshipbatch.FieldUpdatedAt + case "internship_id": + return internshipbatch.FieldInternshipID + case "name": + return internshipbatch.FieldName + case "description": + return internshipbatch.FieldDescription + case "start_date": + return internshipbatch.FieldStartDate + case "end_date": + return internshipbatch.FieldEndDate + case "batch_status": + return internshipbatch.FieldBatchStatus + case "created_by": + return internshipbatch.FieldCreatedBy + case "updated_by": + return internshipbatch.FieldUpdatedBy + default: + return field + } +} + +func (o InternshipBatchQueryOptions) ApplyBaseFilters( + _ context.Context, + query InternshipBatchQuery, + filter *types.InternshipBatchFilter, +) InternshipBatchQuery { + if filter == nil { + return query.Where(internshipbatch.StatusNotIn(string(types.StatusDeleted))) + } + + // Apply status filter + query = o.ApplyStatusFilter(query, filter.GetStatus()) + + // Apply pagination + if !filter.IsUnlimited() { + query = o.ApplyPaginationFilter(query, filter.GetLimit(), filter.GetOffset()) + } + + // Apply sorting + query = o.ApplySortFilter(query, filter.GetSort(), filter.GetOrder()) + + return query +} + +func (o InternshipBatchQueryOptions) ApplyEntityQueryOptions( + _ context.Context, + f *types.InternshipBatchFilter, + query InternshipBatchQuery, +) InternshipBatchQuery { + if f == nil { + return query + } + + // Apply internship IDs filter if specified + if len(f.InternshipIDs) > 0 { + query = query.Where(internshipbatch.InternshipIDIn(f.InternshipIDs...)) + } + + // Apply name filter if specified (search in name) + if f.Name != "" { + query = query.Where(internshipbatch.NameContainsFold(f.Name)) + } + + // Apply batch status filter if specified + if f.BatchStatus != "" { + query = query.Where(internshipbatch.BatchStatus(string(f.BatchStatus))) + } + + // Apply start date filter if specified + if f.StartDate != nil { + query = query.Where(internshipbatch.StartDateGTE(*f.StartDate)) + } + + // Apply end date filter if specified + if f.EndDate != nil { + query = query.Where(internshipbatch.EndDateLTE(*f.EndDate)) + } + + // Apply time range filters if specified + if f.TimeRangeFilter != nil { + if f.StartTime != nil { + query = query.Where(internshipbatch.CreatedAtGTE(*f.StartTime)) + } + if f.EndTime != nil { + query = query.Where(internshipbatch.CreatedAtLTE(*f.EndTime)) + } + } + + return query +} diff --git a/internal/repository/ent/internship_enrollment.go b/internal/repository/ent/internship_enrollment.go new file mode 100644 index 0000000..2cf40e9 --- /dev/null +++ b/internal/repository/ent/internship_enrollment.go @@ -0,0 +1,419 @@ +package ent + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/ent/internshipenrollment" + domainInternshipEnrollment "github.com/omkar273/codegeeky/internal/domain/internshipenrollment" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/postgres" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +type internshipEnrollmentRepository struct { + client postgres.IClient + log logger.Logger + queryOpts EnrollmentQueryOptions +} + +func NewInternshipEnrollmentRepository(client postgres.IClient, logger *logger.Logger) domainInternshipEnrollment.Repository { + return &internshipEnrollmentRepository{ + client: client, + log: *logger, + queryOpts: EnrollmentQueryOptions{}, + } +} + +func (r *internshipEnrollmentRepository) Create(ctx context.Context, enrollmentData *domainInternshipEnrollment.InternshipEnrollment) error { + client := r.client.Querier(ctx) + + r.log.Debugw("creating enrollment", + "enrollment_id", enrollmentData.ID, + "user_id", enrollmentData.UserID, + "internship_id", enrollmentData.InternshipID, + ) + + _, err := client.InternshipEnrollment.Create(). + SetID(enrollmentData.ID). + SetUserID(enrollmentData.UserID). + SetInternshipID(enrollmentData.InternshipID). + SetEnrollmentStatus(enrollmentData.EnrollmentStatus). + SetPaymentStatus(enrollmentData.PaymentStatus). + SetNillableEnrolledAt(enrollmentData.EnrolledAt). + SetNillablePaymentID(enrollmentData.PaymentID). + SetNillableRefundedAt(enrollmentData.RefundedAt). + SetNillableCancellationReason(enrollmentData.CancellationReason). + SetNillableRefundReason(enrollmentData.RefundReason). + SetMetadata(enrollmentData.Metadata). + SetStatus(string(types.StatusPublished)). + SetCreatedAt(enrollmentData.CreatedAt). + SetUpdatedAt(enrollmentData.UpdatedAt). + SetCreatedBy(enrollmentData.CreatedBy). + SetUpdatedBy(enrollmentData.UpdatedBy). + Save(ctx) + + if err != nil { + if ent.IsConstraintError(err) { + return ierr.WithError(err). + WithHint("Enrollment with this user and internship combination already exists"). + WithReportableDetails(map[string]any{ + "enrollment_id": enrollmentData.ID, + "user_id": enrollmentData.UserID, + "internship_id": enrollmentData.InternshipID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create enrollment"). + WithReportableDetails(map[string]any{ + "enrollment_id": enrollmentData.ID, + "user_id": enrollmentData.UserID, + "internship_id": enrollmentData.InternshipID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *internshipEnrollmentRepository) Get(ctx context.Context, id string) (*domainInternshipEnrollment.InternshipEnrollment, error) { + client := r.client.Querier(ctx) + + r.log.Debugw("getting enrollment", "enrollment_id", id) + + entEnrollment, err := client.InternshipEnrollment.Query(). + Where(internshipenrollment.ID(id)). + Only(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHintf("Enrollment with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "enrollment_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHint("Failed to get enrollment"). + WithReportableDetails(map[string]any{ + "enrollment_id": id, + }). + Mark(ierr.ErrDatabase) + } + + return domainInternshipEnrollment.FromEnt(entEnrollment), nil +} + +func (r *internshipEnrollmentRepository) Update(ctx context.Context, enrollmentData *domainInternshipEnrollment.InternshipEnrollment) error { + client := r.client.Querier(ctx) + + r.log.Debugw("updating enrollment", + "enrollment_id", enrollmentData.ID, + "user_id", enrollmentData.UserID, + "internship_id", enrollmentData.InternshipID, + ) + + _, err := client.InternshipEnrollment.UpdateOneID(enrollmentData.ID). + SetUserID(enrollmentData.UserID). + SetInternshipID(enrollmentData.InternshipID). + SetEnrollmentStatus(enrollmentData.EnrollmentStatus). + SetPaymentStatus(enrollmentData.PaymentStatus). + SetNillableEnrolledAt(enrollmentData.EnrolledAt). + SetNillablePaymentID(enrollmentData.PaymentID). + SetNillableRefundedAt(enrollmentData.RefundedAt). + SetNillableCancellationReason(enrollmentData.CancellationReason). + SetNillableRefundReason(enrollmentData.RefundReason). + SetMetadata(enrollmentData.Metadata). + SetUpdatedAt(time.Now().UTC()). + SetNillableIdempotencyKey(enrollmentData.IdempotencyKey). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return ierr.WithError(err). + WithHintf("Enrollment with ID %s was not found", enrollmentData.ID). + WithReportableDetails(map[string]any{ + "enrollment_id": enrollmentData.ID, + }). + Mark(ierr.ErrNotFound) + } + if ent.IsConstraintError(err) { + return ierr.WithError(err). + WithHint("Enrollment with this user and internship combination already exists"). + WithReportableDetails(map[string]any{ + "enrollment_id": enrollmentData.ID, + "user_id": enrollmentData.UserID, + "internship_id": enrollmentData.InternshipID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to update enrollment"). + WithReportableDetails(map[string]any{ + "enrollment_id": enrollmentData.ID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *internshipEnrollmentRepository) Delete(ctx context.Context, id string) error { + client := r.client.Querier(ctx) + + r.log.Debugw("deleting enrollment", + "enrollment_id", id, + ) + + _, err := client.InternshipEnrollment.UpdateOneID(id). + SetStatus(string(types.StatusDeleted)). + SetUpdatedAt(time.Now().UTC()). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return ierr.WithError(err). + WithHintf("Enrollment with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "enrollment_id": id, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHint("Failed to delete enrollment"). + WithReportableDetails(map[string]any{ + "enrollment_id": id, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *internshipEnrollmentRepository) Count(ctx context.Context, filter *types.InternshipEnrollmentFilter) (int, error) { + client := r.client.Querier(ctx) + + r.log.Debugw("counting enrollments") + + query := client.InternshipEnrollment.Query() + query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) + query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) + + count, err := query.Count(ctx) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count enrollments"). + Mark(ierr.ErrDatabase) + } + + return count, nil +} + +func (r *internshipEnrollmentRepository) List(ctx context.Context, filter *types.InternshipEnrollmentFilter) ([]*domainInternshipEnrollment.InternshipEnrollment, error) { + client := r.client.Querier(ctx) + + r.log.Debugw("listing enrollments", + "limit", filter.GetLimit(), + "offset", filter.GetOffset(), + ) + + query := client.InternshipEnrollment.Query() + query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) + query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) + + enrollments, err := query.All(ctx) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list enrollments"). + Mark(ierr.ErrDatabase) + } + + return domainInternshipEnrollment.FromEntList(enrollments), nil +} + +func (r *internshipEnrollmentRepository) ListAll(ctx context.Context, filter *types.InternshipEnrollmentFilter) ([]*domainInternshipEnrollment.InternshipEnrollment, error) { + if filter == nil { + filter = types.NewNoLimitInternshipEnrollmentFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + enrollments, err := r.List(ctx, filter) + if err != nil { + return nil, err + } + return enrollments, nil +} + +func (r *internshipEnrollmentRepository) GetByIdempotencyKey(ctx context.Context, idempotencyKey string) (*domainInternshipEnrollment.InternshipEnrollment, error) { + client := r.client.Querier(ctx) + + r.log.Debugw("getting enrollment by idempotency key", "idempotency_key", idempotencyKey) + + enrollment, err := client.InternshipEnrollment.Query(). + Where(internshipenrollment.IdempotencyKeyEQ(idempotencyKey)). + First(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHintf("Enrollment with idempotency key %s was not found", idempotencyKey). + WithReportableDetails(map[string]any{ + "idempotency_key": idempotencyKey, + }). + Mark(ierr.ErrNotFound) + } + return nil, err + } + + return domainInternshipEnrollment.FromEnt(enrollment), nil +} + +// EnrollmentQuery type alias for better readability +type EnrollmentQuery = *ent.InternshipEnrollmentQuery + +// EnrollmentQueryOptions implements query options for enrollment queries +type EnrollmentQueryOptions struct { + QueryOptionsHelper +} + +// Ensure EnrollmentQueryOptions implements EntityQueryOptions interface +var _ EntityQueryOptions[EnrollmentQuery, *types.InternshipEnrollmentFilter] = (*EnrollmentQueryOptions)(nil) + +func (o EnrollmentQueryOptions) ApplyStatusFilter(query EnrollmentQuery, status string) EnrollmentQuery { + if status == "" { + return query.Where(internshipenrollment.StatusNotIn(string(types.StatusDeleted))) + } + return query.Where(internshipenrollment.Status(status)) +} + +func (o EnrollmentQueryOptions) ApplySortFilter(query EnrollmentQuery, field string, order string) EnrollmentQuery { + field, order = o.ValidateSort(field, order) + fieldName := o.GetFieldName(field) + if order == types.OrderDesc { + return query.Order(ent.Desc(fieldName)) + } + return query.Order(ent.Asc(fieldName)) +} + +func (o EnrollmentQueryOptions) ApplyPaginationFilter(query EnrollmentQuery, limit int, offset int) EnrollmentQuery { + limit, offset = o.ValidatePagination(limit, offset) + return query.Offset(offset).Limit(limit) +} + +func (o EnrollmentQueryOptions) GetFieldName(field string) string { + switch field { + case "created_at": + return internshipenrollment.FieldCreatedAt + case "updated_at": + return internshipenrollment.FieldUpdatedAt + case "user_id": + return internshipenrollment.FieldUserID + case "internship_id": + return internshipenrollment.FieldInternshipID + case "enrollment_status": + return internshipenrollment.FieldEnrollmentStatus + case "payment_status": + return internshipenrollment.FieldPaymentStatus + case "enrolled_at": + return internshipenrollment.FieldEnrolledAt + case "payment_id": + return internshipenrollment.FieldPaymentID + case "refunded_at": + return internshipenrollment.FieldRefundedAt + case "cancellation_reason": + return internshipenrollment.FieldCancellationReason + case "refund_reason": + return internshipenrollment.FieldRefundReason + case "created_by": + return internshipenrollment.FieldCreatedBy + case "updated_by": + return internshipenrollment.FieldUpdatedBy + default: + return field + } +} + +func (o EnrollmentQueryOptions) ApplyBaseFilters( + _ context.Context, + query EnrollmentQuery, + filter *types.InternshipEnrollmentFilter, +) EnrollmentQuery { + if filter == nil { + return query.Where(internshipenrollment.StatusNotIn(string(types.StatusDeleted))) + } + + // Apply status filter + query = o.ApplyStatusFilter(query, filter.GetStatus()) + + // Apply pagination + if !filter.IsUnlimited() { + query = o.ApplyPaginationFilter(query, filter.GetLimit(), filter.GetOffset()) + } + + // Apply sorting + query = o.ApplySortFilter(query, filter.GetSort(), filter.GetOrder()) + + return query +} + +func (o EnrollmentQueryOptions) ApplyEntityQueryOptions( + _ context.Context, + f *types.InternshipEnrollmentFilter, + query EnrollmentQuery, +) EnrollmentQuery { + if f == nil { + return query + } + + // Apply internship IDs filter if specified + if len(f.InternshipIDs) > 0 { + query = query.Where(internshipenrollment.InternshipIDIn(f.InternshipIDs...)) + } + + // Apply user ID filter if specified + if f.UserID != "" { + query = query.Where(internshipenrollment.UserID(f.UserID)) + } + + // Apply enrollment status filter if specified + if f.EnrollmentStatus != "" { + query = query.Where(internshipenrollment.EnrollmentStatus(f.EnrollmentStatus)) + } + + // Apply payment status filter if specified + if f.PaymentStatus != "" { + query = query.Where(internshipenrollment.PaymentStatus(f.PaymentStatus)) + } + + // Apply enrollment IDs filter if specified + if len(f.EnrollmentIDs) > 0 { + query = query.Where(internshipenrollment.IDIn(f.EnrollmentIDs...)) + } + + // Apply payment ID filter if specified + if f.PaymentID != nil && *f.PaymentID != "" { + query = query.Where(internshipenrollment.PaymentID(lo.FromPtr(f.PaymentID))) + } + + // Apply time range filters if specified + if f.TimeRangeFilter != nil { + if f.StartTime != nil { + query = query.Where(internshipenrollment.CreatedAtGTE(*f.StartTime)) + } + if f.EndTime != nil { + query = query.Where(internshipenrollment.CreatedAtLTE(*f.EndTime)) + } + } + + return query +} diff --git a/internal/repository/ent/list_predicates.go b/internal/repository/ent/list_predicates.go deleted file mode 100644 index 07a8667..0000000 --- a/internal/repository/ent/list_predicates.go +++ /dev/null @@ -1,81 +0,0 @@ -package ent - -import ( - "entgo.io/ent/dialect/sql" -) - -// ListPredicateBuilder provides a fluent interface for building list predicates -type ListPredicateBuilder struct { - field string -} - -// NewListPredicateBuilder creates a new ListPredicateBuilder for the given field -func NewListPredicateBuilder(field string) *ListPredicateBuilder { - return &ListPredicateBuilder{ - field: field, - } -} - -// Contains creates a predicate that checks if the list contains the given value -func (b *ListPredicateBuilder) Contains(value string) func(*sql.Selector) { - return func(s *sql.Selector) { - s.Where(sql.Contains(b.field, value)) - } -} - -// ContainsFold creates a case-insensitive predicate that checks if the list contains the given value -func (b *ListPredicateBuilder) ContainsFold(value string) func(*sql.Selector) { - return func(s *sql.Selector) { - s.Where(sql.ContainsFold(b.field, value)) - } -} - -// NotContains creates a predicate that checks if the list does not contain the given value -func (b *ListPredicateBuilder) NotContains(value string) func(*sql.Selector) { - return func(s *sql.Selector) { - s.Where(sql.Not(sql.Contains(b.field, value))) - } -} - -// NotContainsFold creates a case-insensitive predicate that checks if the list does not contain the given value -func (b *ListPredicateBuilder) NotContainsFold(value string) func(*sql.Selector) { - return func(s *sql.Selector) { - s.Where(sql.Not(sql.ContainsFold(b.field, value))) - } -} - -// In creates a predicate that checks if the list contains any of the given values -func (b *ListPredicateBuilder) In(values ...string) func(*sql.Selector) { - return func(s *sql.Selector) { - args := make([]interface{}, len(values)) - for i, v := range values { - args[i] = v - } - s.Where(sql.In(b.field, args...)) - } -} - -// NotIn creates a predicate that checks if the list does not contain any of the given values -func (b *ListPredicateBuilder) NotIn(values ...string) func(*sql.Selector) { - return func(s *sql.Selector) { - args := make([]interface{}, len(values)) - for i, v := range values { - args[i] = v - } - s.Where(sql.NotIn(b.field, args...)) - } -} - -// IsNil creates a predicate that checks if the list is nil -func (b *ListPredicateBuilder) IsNil() func(*sql.Selector) { - return func(s *sql.Selector) { - s.Where(sql.IsNull(b.field)) - } -} - -// NotNil creates a predicate that checks if the list is not nil -func (b *ListPredicateBuilder) NotNil() func(*sql.Selector) { - return func(s *sql.Selector) { - s.Where(sql.NotNull(b.field)) - } -} diff --git a/internal/repository/ent/payment.go b/internal/repository/ent/payment.go new file mode 100644 index 0000000..5704429 --- /dev/null +++ b/internal/repository/ent/payment.go @@ -0,0 +1,551 @@ +package ent + +import ( + "context" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/ent/payment" + "github.com/omkar273/codegeeky/ent/paymentattempt" + domainPayment "github.com/omkar273/codegeeky/internal/domain/payment" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/postgres" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +type paymentRepository struct { + client postgres.IClient + log logger.Logger + queryOpts PaymentQueryOptions +} + +func NewPaymentRepository(client postgres.IClient, logger *logger.Logger) domainPayment.Repository { + return &paymentRepository{ + client: client, + log: *logger, + queryOpts: PaymentQueryOptions{}, + } +} + +func (r *paymentRepository) Count(ctx context.Context, filter *types.PaymentFilter) (int, error) { + client := r.client.Querier(ctx) + query := client.Payment.Query() + query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) + query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) + + count, err := query.Count(ctx) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count payments"). + Mark(ierr.ErrDatabase) + } + + return count, nil +} + +func (r *paymentRepository) Create(ctx context.Context, p *domainPayment.Payment) error { + if err := p.Validate(); err != nil { + return err + } + + client := r.client.Querier(ctx) + + builder := client.Payment.Create(). + SetID(p.ID). + SetIdempotencyKey(p.IdempotencyKey). + SetDestinationType(p.DestinationType). + SetDestinationID(p.DestinationID). + SetPaymentMethodID(p.PaymentMethodID). + SetPaymentGatewayProvider(p.PaymentGatewayProvider). + SetAmount(p.Amount). + SetCurrency(p.Currency). + SetPaymentStatus(p.PaymentStatus). + SetTrackAttempts(p.TrackAttempts). + SetMetadata(p.Metadata). + SetStatus(string(types.StatusPublished)). + SetCreatedAt(p.CreatedAt). + SetUpdatedAt(p.UpdatedAt). + SetCreatedBy(p.CreatedBy). + SetNillableGatewayPaymentID(p.GatewayPaymentID). + SetNillableSucceededAt(p.SucceededAt). + SetNillableFailedAt(p.FailedAt). + SetNillableRefundedAt(p.RefundedAt). + SetNillableErrorMessage(p.ErrorMessage). + SetUpdatedBy(p.UpdatedBy). + SetNillablePaymentMethodType(p.PaymentMethodType). + SetNillablePaymentMethodID(lo.ToPtr(p.PaymentMethodID)) + + if _, err := builder.Save(ctx); err != nil { + if ent.IsConstraintError(err) { + return ierr.WithError(err). + WithHint("Payment with this idempotency key already exists"). + WithReportableDetails(map[string]any{ + "idempotency_key": p.IdempotencyKey, + }). + Mark(ierr.ErrAlreadyExists) + } + + return ierr.WithError(err). + WithHint("Failed to create payment"). + WithReportableDetails(map[string]any{ + "payment": p, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *paymentRepository) Get(ctx context.Context, id string) (*domainPayment.Payment, error) { + client := r.client.Querier(ctx) + payment, err := client.Payment.Query(). + Where(payment.ID(id)). + WithAttempts(). + First(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHint("Payment with this ID not found"). + WithReportableDetails(map[string]any{ + "payment_id": id, + }). + Mark(ierr.ErrNotFound) + } + + return nil, ierr.WithError(err). + WithHint("Failed to get payment"). + WithReportableDetails(map[string]any{ + "payment_id": id, + }). + Mark(ierr.ErrDatabase) + } + + return domainPayment.FromEnt(payment), nil +} + +func (r *paymentRepository) List(ctx context.Context, filter *types.PaymentFilter) ([]*domainPayment.Payment, error) { + client := r.client.Querier(ctx) + query := client.Payment.Query() + query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) + query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) + + payments, err := query.WithAttempts().All(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHint("Payments not found"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHint("Failed to get payments"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + + return domainPayment.FromEntList(payments), nil +} + +func (r *paymentRepository) Update(ctx context.Context, payment *domainPayment.Payment) error { + if err := payment.Validate(); err != nil { + return err + } + + client := r.client.Querier(ctx) + + builder := client.Payment.UpdateOneID(payment.ID). + SetPaymentStatus(payment.PaymentStatus). + SetMetadata(payment.Metadata). + SetUpdatedBy(types.GetUserID(ctx)) + + if payment.PaymentMethodType != nil { + builder = builder.SetPaymentMethodType(*payment.PaymentMethodType) + } + + if payment.GatewayPaymentID != nil { + builder = builder.SetGatewayPaymentID(*payment.GatewayPaymentID) + } + + if payment.SucceededAt != nil { + builder = builder.SetSucceededAt(*payment.SucceededAt) + } + + if payment.FailedAt != nil { + builder = builder.SetFailedAt(*payment.FailedAt) + } + + if payment.RefundedAt != nil { + builder = builder.SetRefundedAt(*payment.RefundedAt) + } + + if payment.ErrorMessage != nil { + builder = builder.SetErrorMessage(*payment.ErrorMessage) + } + + _, err := builder.Save(ctx) + + if err != nil { + return ierr.WithError(err). + WithHint("Failed to update payment"). + WithReportableDetails(map[string]any{ + "payment_id": payment.ID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *paymentRepository) Delete(ctx context.Context, id string) error { + client := r.client.Querier(ctx) + _, err := client.Payment.UpdateOneID(id). + SetStatus(string(types.StatusDeleted)). + SetUpdatedBy(types.GetUserID(ctx)). + Save(ctx) + + if err != nil { + return ierr.WithError(err). + WithHint("Failed to delete payment"). + WithReportableDetails(map[string]any{ + "payment_id": id, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *paymentRepository) GetByIdempotencyKey(ctx context.Context, key string) (*domainPayment.Payment, error) { + client := r.client.Querier(ctx) + payment, err := client.Payment.Query(). + Where(payment.IdempotencyKey(key)). + WithAttempts(). + First(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHint("Payment with this idempotency key not found"). + WithReportableDetails(map[string]any{ + "idempotency_key": key, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHint("Failed to get payment"). + WithReportableDetails(map[string]any{ + "idempotency_key": key, + }). + Mark(ierr.ErrDatabase) + } + + return domainPayment.FromEnt(payment), nil +} + +// Payment attempt operations +func (r *paymentRepository) CreateAttempt(ctx context.Context, attempt *domainPayment.PaymentAttempt) error { + if err := attempt.Validate(); err != nil { + return err + } + + client := r.client.Querier(ctx) + + builder := client.PaymentAttempt.Create(). + SetID(attempt.ID). + SetPaymentID(attempt.PaymentID). + SetAttemptNumber(attempt.AttemptNumber). + SetPaymentStatus(string(attempt.PaymentStatus)). + SetMetadata(attempt.Metadata). + SetStatus(string(types.StatusPublished)). + SetCreatedAt(attempt.CreatedAt). + SetUpdatedAt(attempt.UpdatedAt). + SetCreatedBy(attempt.CreatedBy). + SetUpdatedBy(attempt.UpdatedBy) + + if attempt.GatewayAttemptID != nil { + builder = builder.SetGatewayAttemptID(*attempt.GatewayAttemptID) + } + + if attempt.ErrorMessage != nil { + builder = builder.SetErrorMessage(*attempt.ErrorMessage) + } + + _, err := builder.Save(ctx) + + if err != nil { + return ierr.WithError(err). + WithHint("Failed to create payment attempt"). + WithReportableDetails(map[string]any{ + "attempt": attempt, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *paymentRepository) GetAttempt(ctx context.Context, id string) (*domainPayment.PaymentAttempt, error) { + client := r.client.Querier(ctx) + attempt, err := client.PaymentAttempt.Query(). + Where(paymentattempt.ID(id)). + First(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHint("Payment attempt with this ID not found"). + WithReportableDetails(map[string]any{ + "attempt_id": id, + }). + Mark(ierr.ErrNotFound) + } + + return nil, ierr.WithError(err). + WithHint("Failed to get payment attempt"). + WithReportableDetails(map[string]any{ + "attempt_id": id, + }). + Mark(ierr.ErrDatabase) + } + + return domainPayment.FromEntAttempt(attempt), nil +} + +func (r *paymentRepository) UpdateAttempt(ctx context.Context, attempt *domainPayment.PaymentAttempt) error { + if err := attempt.Validate(); err != nil { + return err + } + + client := r.client.Querier(ctx) + + builder := client.PaymentAttempt.UpdateOneID(attempt.ID). + SetPaymentStatus(string(attempt.PaymentStatus)). + SetMetadata(attempt.Metadata). + SetUpdatedBy(types.GetUserID(ctx)) + + if attempt.GatewayAttemptID != nil { + builder = builder.SetGatewayAttemptID(*attempt.GatewayAttemptID) + } + + if attempt.ErrorMessage != nil { + builder = builder.SetErrorMessage(*attempt.ErrorMessage) + } + + _, err := builder.Save(ctx) + + if err != nil { + return ierr.WithError(err). + WithHint("Failed to update payment attempt"). + WithReportableDetails(map[string]any{ + "attempt_id": attempt.ID, + }). + Mark(ierr.ErrDatabase) + } + + return nil +} + +func (r *paymentRepository) ListAttempts(ctx context.Context, paymentID string) ([]*domainPayment.PaymentAttempt, error) { + client := r.client.Querier(ctx) + attempts, err := client.PaymentAttempt.Query(). + Where(paymentattempt.PaymentID(paymentID)). + Where(paymentattempt.StatusNotIn(string(types.StatusDeleted))). + Order(ent.Desc(paymentattempt.FieldAttemptNumber)). + All(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHint("Payment attempts not found"). + WithReportableDetails(map[string]any{ + "payment_id": paymentID, + }). + Mark(ierr.ErrNotFound) + } + + return nil, ierr.WithError(err). + WithHint("Failed to get payment attempts"). + WithReportableDetails(map[string]any{ + "payment_id": paymentID, + }). + Mark(ierr.ErrDatabase) + } + + return domainPayment.FromEntAttemptList(attempts), nil +} + +func (r *paymentRepository) GetLatestAttempt(ctx context.Context, paymentID string) (*domainPayment.PaymentAttempt, error) { + client := r.client.Querier(ctx) + attempt, err := client.PaymentAttempt.Query(). + Where(paymentattempt.PaymentID(paymentID)). + Where(paymentattempt.StatusNotIn(string(types.StatusDeleted))). + Order(ent.Desc(paymentattempt.FieldAttemptNumber)). + First(ctx) + + if err != nil { + if ent.IsNotFound(err) { + return nil, ierr.WithError(err). + WithHint("Latest payment attempt not found"). + WithReportableDetails(map[string]any{ + "payment_id": paymentID, + }). + Mark(ierr.ErrNotFound) + } + + return nil, ierr.WithError(err). + WithHint("Failed to get latest payment attempt"). + WithReportableDetails(map[string]any{ + "payment_id": paymentID, + }). + Mark(ierr.ErrDatabase) + } + + return domainPayment.FromEntAttempt(attempt), nil +} + +// PaymentQuery type alias for better readability +type PaymentQuery = *ent.PaymentQuery + +// PaymentQueryOptions implements query options for payment queries +type PaymentQueryOptions struct { + QueryOptionsHelper +} + +// Ensure PaymentQueryOptions implements EntityQueryOptions interface +var _ EntityQueryOptions[PaymentQuery, *types.PaymentFilter] = (*PaymentQueryOptions)(nil) + +func (o PaymentQueryOptions) ApplyStatusFilter(query PaymentQuery, status string) PaymentQuery { + if status == "" { + return query.Where(payment.StatusNotIn(string(types.StatusDeleted))) + } + return query.Where(payment.Status(status)) +} + +func (o PaymentQueryOptions) ApplySortFilter(query PaymentQuery, field string, order string) PaymentQuery { + field, order = o.ValidateSort(field, order) + fieldName := o.GetFieldName(field) + if order == types.OrderDesc { + return query.Order(ent.Desc(fieldName)) + } + return query.Order(ent.Asc(fieldName)) +} + +func (o PaymentQueryOptions) ApplyPaginationFilter(query PaymentQuery, limit int, offset int) PaymentQuery { + limit, offset = o.ValidatePagination(limit, offset) + return query.Offset(offset).Limit(limit) +} + +func (o PaymentQueryOptions) GetFieldName(field string) string { + switch field { + case "created_at": + return payment.FieldCreatedAt + case "updated_at": + return payment.FieldUpdatedAt + case "idempotency_key": + return payment.FieldIdempotencyKey + case "destination_type": + return payment.FieldDestinationType + case "destination_id": + return payment.FieldDestinationID + case "payment_method_type": + return payment.FieldPaymentMethodType + case "payment_method_id": + return payment.FieldPaymentMethodID + case "payment_gateway_provider": + return payment.FieldPaymentGatewayProvider + case "gateway_payment_id": + return payment.FieldGatewayPaymentID + case "amount": + return payment.FieldAmount + case "currency": + return payment.FieldCurrency + case "payment_status": + return payment.FieldPaymentStatus + case "track_attempts": + return payment.FieldTrackAttempts + case "succeeded_at": + return payment.FieldSucceededAt + case "failed_at": + return payment.FieldFailedAt + case "refunded_at": + return payment.FieldRefundedAt + case "error_message": + return payment.FieldErrorMessage + case "created_by": + return payment.FieldCreatedBy + case "updated_by": + return payment.FieldUpdatedBy + default: + return "" + } +} + +func (o PaymentQueryOptions) ApplyBaseFilters( + _ context.Context, + query PaymentQuery, + filter *types.PaymentFilter, +) PaymentQuery { + if filter == nil { + return query.Where(payment.StatusNotIn(string(types.StatusDeleted))) + } + + // Apply status filter + query = o.ApplyStatusFilter(query, filter.GetStatus()) + + // Apply pagination + if !filter.IsUnlimited() { + query = o.ApplyPaginationFilter(query, filter.GetLimit(), filter.GetOffset()) + } + + // Apply sorting + query = o.ApplySortFilter(query, filter.GetSort(), filter.GetOrder()) + + return query +} + +func (o PaymentQueryOptions) ApplyEntityQueryOptions( + _ context.Context, + f *types.PaymentFilter, + query PaymentQuery, +) PaymentQuery { + if f == nil { + return query + } + + if f.PaymentIDs != nil { + query = query.Where(payment.IDIn(f.PaymentIDs...)) + } + + if f.DestinationType != nil { + query = query.Where(payment.DestinationType(types.PaymentDestinationType(*f.DestinationType))) + } + + if f.DestinationID != nil { + query = query.Where(payment.DestinationID(*f.DestinationID)) + } + + if f.PaymentMethodType != nil { + query = query.Where(payment.PaymentMethodType(types.PaymentMethodType(*f.PaymentMethodType))) + } + + if f.PaymentStatus != nil { + query = query.Where(payment.PaymentStatus(types.PaymentStatus(*f.PaymentStatus))) + } + + if f.PaymentGateway != nil { + query = query.Where(payment.PaymentGatewayProvider(types.PaymentGatewayProvider(*f.PaymentGateway))) + } + + if f.Currency != nil { + query = query.Where(payment.Currency(types.Currency(*f.Currency))) + } + + return query +} diff --git a/internal/repository/ent/rank.go b/internal/repository/ent/rank.go deleted file mode 100644 index 1316736..0000000 --- a/internal/repository/ent/rank.go +++ /dev/null @@ -1,380 +0,0 @@ -package ent - -// import ( -// "context" -// "time" - -// "github.com/omkar273/codegeeky/ent" -// "github.com/omkar273/codegeeky/ent/rank" -// domainRank "github.com/omkar273/codegeeky/internal/domain/rank" -// ierr "github.com/omkar273/codegeeky/internal/errors" -// "github.com/omkar273/codegeeky/internal/logger" -// "github.com/omkar273/codegeeky/internal/postgres" -// "github.com/omkar273/codegeeky/internal/types" -// ) - -// type RankRepository struct { -// client postgres.IClient -// log logger.Logger -// queryOpts RankQueryOptions -// } - -// func NewRankRepository(client postgres.IClient, logger *logger.Logger) domainRank.Repository { -// return &RankRepository{ -// client: client, -// log: *logger, -// queryOpts: RankQueryOptions{}, -// } -// } - -// func (r *RankRepository) Create(ctx context.Context, rankData *domainRank.Rank) (*domainRank.Rank, error) { -// client := r.client.Querier(ctx) - -// r.log.Debugw("creating rank", -// "rank_id", rankData.ID, -// "name", rankData.Name, -// "code", rankData.Code, -// ) - -// entRank, err := client.Rank.Create(). -// SetName(rankData.Name). -// SetDescription(rankData.Description). -// SetAbbreviation(rankData.Abbreviation). -// SetCode(rankData.Code). -// SetHierarchyLevel(rankData.HierarchyLevel). -// SetStatus(string(rankData.Status)). -// SetCreatedAt(rankData.CreatedAt). -// SetUpdatedAt(rankData.UpdatedAt). -// SetCreatedBy(rankData.CreatedBy). -// SetUpdatedBy(rankData.UpdatedBy). -// Save(ctx) - -// if err != nil { -// if ent.IsConstraintError(err) { -// return nil, ierr.WithError(err). -// WithHint("Rank with this code already exists"). -// WithReportableDetails(map[string]any{ -// "rank_id": rankData.ID, -// "rank_code": rankData.Code, -// }). -// Mark(ierr.ErrAlreadyExists) -// } -// return nil, ierr.WithError(err). -// WithHint("Failed to create rank"). -// WithReportableDetails(map[string]any{ -// "rank_id": rankData.ID, -// "rank_name": rankData.Name, -// }). -// Mark(ierr.ErrDatabase) -// } - -// return domainRank.FromEnt(entRank), nil -// } - -// func (r *RankRepository) Get(ctx context.Context, id string) (*domainRank.Rank, error) { -// client := r.client.Querier(ctx) - -// r.log.Debugw("getting rank", "rank_id", id) - -// entRank, err := client.Rank.Query(). -// Where(rank.ID(id)). -// Only(ctx) - -// if err != nil { -// if ent.IsNotFound(err) { -// return nil, ierr.WithError(err). -// WithHintf("Rank with ID %s was not found", id). -// WithReportableDetails(map[string]any{ -// "rank_id": id, -// }). -// Mark(ierr.ErrNotFound) -// } -// return nil, ierr.WithError(err). -// WithHint("Failed to get rank"). -// WithReportableDetails(map[string]any{ -// "rank_id": id, -// }). -// Mark(ierr.ErrDatabase) -// } - -// return domainRank.FromEnt(entRank), nil -// } - -// func (r *RankRepository) GetByCode(ctx context.Context, code string) (*domainRank.Rank, error) { -// client := r.client.Querier(ctx) - -// r.log.Debugw("getting rank by code", "code", code) - -// entRank, err := client.Rank.Query(). -// Where( -// rank.Code(code), -// rank.StatusNotIn(string(types.StatusDeleted)), -// ). -// Only(ctx) - -// if err != nil { -// if ent.IsNotFound(err) { -// return nil, ierr.WithError(err). -// WithHintf("Rank with code %s was not found", code). -// WithReportableDetails(map[string]any{ -// "rank_code": code, -// }). -// Mark(ierr.ErrNotFound) -// } -// return nil, ierr.WithError(err). -// WithHint("Failed to get rank by code"). -// WithReportableDetails(map[string]any{ -// "rank_code": code, -// }). -// Mark(ierr.ErrDatabase) -// } - -// return domainRank.FromEnt(entRank), nil -// } - -// func (r *RankRepository) List(ctx context.Context, filter *types.RankFilter) ([]*domainRank.Rank, error) { -// client := r.client.Querier(ctx) - -// r.log.Debugw("listing ranks", -// "limit", filter.GetLimit(), -// "offset", filter.GetOffset(), -// ) - -// query := client.Rank.Query() -// query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) -// query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) - -// ranks, err := query.All(ctx) -// if err != nil { -// return nil, ierr.WithError(err). -// WithHint("Failed to list ranks"). -// Mark(ierr.ErrDatabase) -// } - -// return domainRank.FromEntList(ranks), nil -// } - -// func (r *RankRepository) ListAll(ctx context.Context, filter *types.RankFilter) ([]*domainRank.Rank, error) { -// if filter == nil { -// filter = types.NewNoLimitRankFilter() -// } - -// if filter.QueryFilter == nil { -// filter.QueryFilter = types.NewNoLimitQueryFilter() -// } - -// ranks, err := r.List(ctx, filter) -// if err != nil { -// return nil, err -// } -// return ranks, nil -// } - -// func (r *RankRepository) Count(ctx context.Context, filter *types.RankFilter) (int, error) { -// client := r.client.Querier(ctx) - -// r.log.Debugw("counting ranks") - -// query := client.Rank.Query() -// query = r.queryOpts.ApplyBaseFilters(ctx, query, filter) -// query = r.queryOpts.ApplyEntityQueryOptions(ctx, filter, query) - -// count, err := query.Count(ctx) -// if err != nil { -// return 0, ierr.WithError(err). -// WithHint("Failed to count ranks"). -// Mark(ierr.ErrDatabase) -// } - -// return count, nil -// } - -// func (r *RankRepository) Update(ctx context.Context, rankData *domainRank.Rank) (*domainRank.Rank, error) { -// client := r.client.Querier(ctx) - -// r.log.Debugw("updating rank", -// "rank_id", rankData.ID, -// "name", rankData.Name, -// ) - -// entRank, err := client.Rank.UpdateOneID(rankData.ID). -// SetName(rankData.Name). -// SetDescription(rankData.Description). -// SetAbbreviation(rankData.Abbreviation). -// SetCode(rankData.Code). -// SetHierarchyLevel(rankData.HierarchyLevel). -// SetUpdatedAt(time.Now().UTC()). -// SetUpdatedBy(rankData.UpdatedBy). -// Save(ctx) - -// if err != nil { -// if ent.IsNotFound(err) { -// return nil, ierr.WithError(err). -// WithHintf("Rank with ID %s was not found", rankData.ID). -// WithReportableDetails(map[string]any{ -// "rank_id": rankData.ID, -// }). -// Mark(ierr.ErrNotFound) -// } -// if ent.IsConstraintError(err) { -// return nil, ierr.WithError(err). -// WithHint("Rank with this code already exists"). -// WithReportableDetails(map[string]any{ -// "rank_id": rankData.ID, -// "rank_code": rankData.Code, -// }). -// Mark(ierr.ErrAlreadyExists) -// } -// return nil, ierr.WithError(err). -// WithHint("Failed to update rank"). -// WithReportableDetails(map[string]any{ -// "rank_id": rankData.ID, -// }). -// Mark(ierr.ErrDatabase) -// } - -// return domainRank.FromEnt(entRank), nil -// } - -// func (r *RankRepository) Delete(ctx context.Context, rankData *domainRank.Rank) error { -// client := r.client.Querier(ctx) - -// r.log.Debugw("deleting rank", -// "rank_id", rankData.ID, -// ) - -// _, err := client.Rank.UpdateOneID(rankData.ID). -// SetStatus(string(types.StatusDeleted)). -// SetUpdatedAt(time.Now().UTC()). -// SetUpdatedBy(rankData.UpdatedBy). -// Save(ctx) - -// if err != nil { -// if ent.IsNotFound(err) { -// return ierr.WithError(err). -// WithHintf("Rank with ID %s was not found", rankData.ID). -// WithReportableDetails(map[string]any{ -// "rank_id": rankData.ID, -// }). -// Mark(ierr.ErrNotFound) -// } -// return ierr.WithError(err). -// WithHint("Failed to delete rank"). -// WithReportableDetails(map[string]any{ -// "rank_id": rankData.ID, -// }). -// Mark(ierr.ErrDatabase) -// } - -// return nil -// } - -// // RankQuery type alias for better readability -// type RankQuery = *ent.RankQuery - -// // RankQueryOptions implements query options for rank queries -// type RankQueryOptions struct { -// QueryOptionsHelper -// } - -// // Ensure RankQueryOptions implements EntityQueryOptions interface -// var _ EntityQueryOptions[RankQuery, *types.RankFilter] = (*RankQueryOptions)(nil) - -// func (o RankQueryOptions) ApplyStatusFilter(query RankQuery, status string) RankQuery { -// if status == "" { -// return query.Where(rank.StatusNotIn(string(types.StatusDeleted))) -// } -// return query.Where(rank.Status(status)) -// } - -// func (o RankQueryOptions) ApplySortFilter(query RankQuery, field string, order string) RankQuery { -// field, order = o.ValidateSort(field, order) -// fieldName := o.GetFieldName(field) -// if order == types.OrderDesc { -// return query.Order(ent.Desc(fieldName)) -// } -// return query.Order(ent.Asc(fieldName)) -// } - -// func (o RankQueryOptions) ApplyPaginationFilter(query RankQuery, limit int, offset int) RankQuery { -// limit, offset = o.ValidatePagination(limit, offset) -// return query.Offset(offset).Limit(limit) -// } - -// func (o RankQueryOptions) GetFieldName(field string) string { -// switch field { -// case "created_at": -// return rank.FieldCreatedAt -// case "updated_at": -// return rank.FieldUpdatedAt -// case "name": -// return rank.FieldName -// case "code": -// return rank.FieldCode -// case "hierarchy_level": -// return rank.FieldHierarchyLevel -// default: -// return field -// } -// } - -// func (o RankQueryOptions) ApplyBaseFilters( -// _ context.Context, -// query RankQuery, -// filter *types.RankFilter, -// ) RankQuery { -// if filter == nil { -// return query.Where(rank.StatusNotIn(string(types.StatusDeleted))) -// } - -// // Apply status filter -// query = o.ApplyStatusFilter(query, filter.GetStatus()) - -// // Apply pagination -// if !filter.IsUnlimited() { -// query = o.ApplyPaginationFilter(query, filter.GetLimit(), filter.GetOffset()) -// } - -// // Apply sorting -// query = o.ApplySortFilter(query, filter.GetSort(), filter.GetOrder()) - -// return query -// } - -// func (o RankQueryOptions) ApplyEntityQueryOptions( -// _ context.Context, -// f *types.RankFilter, -// query RankQuery, -// ) RankQuery { -// if f == nil { -// return query -// } - -// // Apply rank IDs filter if specified -// if len(f.RankIds) > 0 { -// query = query.Where(rank.IDIn(f.RankIds...)) -// } - -// // Apply hierarchy level filter if specified -// if f.Level != nil { -// query = query.Where(rank.HierarchyLevelEQ(*f.Level)) -// } - -// // Apply time range filters if specified -// if f.TimeRangeFilter != nil { -// if f.StartTime != nil { -// query = query.Where(rank.CreatedAtGTE(*f.StartTime)) -// } -// if f.EndTime != nil { -// query = query.Where(rank.CreatedAtLTE(*f.EndTime)) -// } -// } - -// // Apply expansion if requested -// expand := f.GetExpand() -// if expand.Has("users") { -// query = query.WithUsers() -// } - -// return query -// } diff --git a/internal/repository/factory.go b/internal/repository/factory.go index 33d2668..fb6b330 100644 --- a/internal/repository/factory.go +++ b/internal/repository/factory.go @@ -4,6 +4,8 @@ import ( "github.com/omkar273/codegeeky/internal/config" "github.com/omkar273/codegeeky/internal/domain/discount" "github.com/omkar273/codegeeky/internal/domain/internship" + "github.com/omkar273/codegeeky/internal/domain/internshipenrollment" + "github.com/omkar273/codegeeky/internal/domain/payment" "github.com/omkar273/codegeeky/internal/domain/user" "github.com/omkar273/codegeeky/internal/logger" "github.com/omkar273/codegeeky/internal/postgres" @@ -46,3 +48,15 @@ func NewCategoryRepository(params RepositoryParams) internship.CategoryRepositor func NewDiscountRepository(params RepositoryParams) discount.Repository { return ent.NewDiscountRepository(params.Client, params.Logger) } + +func NewPaymentRepository(params RepositoryParams) payment.Repository { + return ent.NewPaymentRepository(params.Client, params.Logger) +} + +func NewInternshipEnrollmentRepository(params RepositoryParams) internshipenrollment.Repository { + return ent.NewInternshipEnrollmentRepository(params.Client, params.Logger) +} + +func NewInternshipBatchRepository(params RepositoryParams) internship.InternshipBatchRepository { + return ent.NewInternshipBatchRepository(params.Client, params.Logger) +} diff --git a/internal/service/README.md b/internal/service/README.md new file mode 100644 index 0000000..d6da295 --- /dev/null +++ b/internal/service/README.md @@ -0,0 +1,142 @@ +# Payment Service + +## Overview + +The Payment Service has been sanitized to focus only on CRUD operations for the payments table. All third-party payment gateway logic has been removed to make it reusable across the system. + +## Features + +### Core Payment Operations + +- **Create**: Create new payment records with idempotency support +- **GetByID**: Retrieve payments by ID +- **GetByIdempotencyKey**: Retrieve payments by idempotency key +- **Update**: Update existing payment records +- **Delete**: Delete payment records +- **List**: Retrieve paginated list of payments with filtering + +### Payment Attempt Operations + +- **CreateAttempt**: Create payment attempt records +- **GetAttempt**: Retrieve payment attempts by ID +- **ListAttempts**: Get all attempts for a payment +- **GetLatestAttempt**: Get the latest attempt for a payment + +### Status Management + +- **UpdateStatus**: Update payment status with metadata +- **MarkAsSuccess**: Mark payment as successful +- **MarkAsFailed**: Mark payment as failed with error message +- **MarkAsRefunded**: Mark payment as refunded + +## Usage + +### Basic Payment Creation + +```go +// Create a payment service +paymentService := NewPaymentService(serviceParams) + +// Create a payment request +req := &dto.CreatePaymentRequest{ + PaymentRequest: dto.PaymentRequest{ + ReferenceID: "enrollment-123", + ReferenceType: types.PaymentDestinationTypeEnrollment, + DestinationID: "internship-456", + DestinationType: types.PaymentDestinationTypeInternship, + Amount: 10000, // 100.00 in paisa + Currency: "INR", + PaymentGatewayProvider: types.PaymentGatewayProviderRazorpay, + PaymentMethodType: &types.PaymentMethodTypeCard, + IdempotencyKey: "unique-key-123", + Metadata: map[string]string{"source": "web"}, + TrackAttempts: true, + }, +} + +// Create the payment +payment, err := paymentService.Create(ctx, req) +if err != nil { + // Handle error +} +``` + +### Status Updates + +```go +// Mark payment as successful +payment, err := paymentService.MarkAsSuccess(ctx, paymentID, &gatewayPaymentID, metadata) + +// Mark payment as failed +payment, err := paymentService.MarkAsFailed(ctx, paymentID, "Payment declined", metadata) + +// Mark payment as refunded +payment, err := paymentService.MarkAsRefunded(ctx, paymentID, metadata) +``` + +### Payment Attempts + +```go +// Create a payment attempt +attemptReq := &dto.PaymentAttemptRequest{ + PaymentID: paymentID, + PaymentStatus: types.PaymentStatusPending, + Metadata: types.MetadataFromEnt(payment.Metadata), +} + +attempt, err := paymentService.CreateAttempt(ctx, attemptReq) + +// Get latest attempt +latestAttempt, err := paymentService.GetLatestAttempt(ctx, paymentID) +``` + +## Integration with External Payment Processing + +The sanitized payment service is designed to work with external payment processing systems: + +1. **Create Payment Record**: Use the service to create a payment record in your database +2. **External Processing**: Handle actual payment processing in a separate service/module +3. **Update Status**: Use the service's status update methods to reflect the payment result + +### Example Workflow + +```go +// 1. Create payment record +payment, err := paymentService.Create(ctx, createReq) + +// 2. Send to external payment processor +// (This would be handled by your external payment service) +externalPaymentID, err := externalPaymentProcessor.Process(payment.ID, payment.Amount) + +// 3. Update payment status based on external processor response +if err != nil { + paymentService.MarkAsFailed(ctx, payment.ID, err.Error(), nil) +} else { + paymentService.MarkAsSuccess(ctx, payment.ID, &externalPaymentID, nil) +} +``` + +## Dependencies + +The payment service depends on: + +- `ServiceParams` (database, logger, repositories) +- Payment repository for data persistence +- No external payment gateway dependencies + +## Benefits + +1. **Reusable**: Can be used across different parts of your system +2. **Testable**: Easy to unit test without external dependencies +3. **Flexible**: Can integrate with any payment processing system +4. **Idempotent**: Supports safe retries with idempotency keys +5. **Auditable**: Tracks payment attempts and status changes + +## Future Enhancements + +When you're ready to implement real payment processing: + +1. Create a separate payment processing service +2. Use the gateway registry (`gateway.GatewayRegistryService`) for third-party integrations +3. Integrate with the payment service for status updates +4. Add webhook handling for payment status updates diff --git a/internal/service/cart.go b/internal/service/cart.go new file mode 100644 index 0000000..6d43c33 --- /dev/null +++ b/internal/service/cart.go @@ -0,0 +1 @@ +package service diff --git a/internal/service/category.go b/internal/service/category.go index c74a27c..9aeb096 100644 --- a/internal/service/category.go +++ b/internal/service/category.go @@ -4,9 +4,7 @@ import ( "context" "github.com/omkar273/codegeeky/internal/api/dto" - domainInternship "github.com/omkar273/codegeeky/internal/domain/internship" ierr "github.com/omkar273/codegeeky/internal/errors" - "github.com/omkar273/codegeeky/internal/logger" "github.com/omkar273/codegeeky/internal/types" ) @@ -19,17 +17,14 @@ type CategoryService interface { } type categoryService struct { - categoryRepo domainInternship.CategoryRepository - logger *logger.Logger + ServiceParams } func NewCategoryService( - categoryRepo domainInternship.CategoryRepository, - logger *logger.Logger, + params ServiceParams, ) CategoryService { return &categoryService{ - categoryRepo: categoryRepo, - logger: logger, + ServiceParams: params, } } @@ -41,7 +36,7 @@ func (s *categoryService) Create(ctx context.Context, req *dto.CreateCategoryReq category := req.ToCategory(ctx) - err := s.categoryRepo.Create(ctx, category) + err := s.CategoryRepo.Create(ctx, category) if err != nil { return nil, err } @@ -52,7 +47,7 @@ func (s *categoryService) Create(ctx context.Context, req *dto.CreateCategoryReq } func (s *categoryService) GetByID(ctx context.Context, id string) (*dto.CategoryResponse, error) { - category, err := s.categoryRepo.Get(ctx, id) + category, err := s.CategoryRepo.Get(ctx, id) if err != nil { return nil, err } @@ -68,7 +63,7 @@ func (s *categoryService) Update(ctx context.Context, id string, req *dto.Update return nil, err } - category, err := s.categoryRepo.Get(ctx, id) + category, err := s.CategoryRepo.Get(ctx, id) if err != nil { return nil, err } @@ -90,7 +85,7 @@ func (s *categoryService) Update(ctx context.Context, id string, req *dto.Update category.LookupKey = req.LookupKey } - err = s.categoryRepo.Update(ctx, category) + err = s.CategoryRepo.Update(ctx, category) if err != nil { return nil, err } @@ -108,12 +103,12 @@ func (s *categoryService) Delete(ctx context.Context, id string) error { Mark(ierr.ErrValidation) } - _, err := s.categoryRepo.Get(ctx, id) + _, err := s.CategoryRepo.Get(ctx, id) if err != nil { return err } - err = s.categoryRepo.Delete(ctx, id) + err = s.CategoryRepo.Delete(ctx, id) if err != nil { return err } @@ -130,13 +125,13 @@ func (s *categoryService) List(ctx context.Context, filter *types.CategoryFilter return nil, err } - count, err := s.categoryRepo.Count(ctx, filter) + count, err := s.CategoryRepo.Count(ctx, filter) if err != nil { return nil, err } // Get categories - categories, err := s.categoryRepo.List(ctx, filter) + categories, err := s.CategoryRepo.List(ctx, filter) if err != nil { return nil, err } diff --git a/internal/service/discount.go b/internal/service/discount.go index 9db0e7e..9b208c0 100644 --- a/internal/service/discount.go +++ b/internal/service/discount.go @@ -2,10 +2,13 @@ package service import ( "context" + "time" "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/domain/internship" ierr "github.com/omkar273/codegeeky/internal/errors" "github.com/omkar273/codegeeky/internal/types" + "github.com/shopspring/decimal" ) type DiscountService interface { @@ -15,6 +18,7 @@ type DiscountService interface { Delete(ctx context.Context, id string) error List(ctx context.Context, filter *types.DiscountFilter) (*dto.ListDiscountResponse, error) GetByCode(ctx context.Context, code string) (*dto.DiscountResponse, error) + ValidateDiscountCode(ctx context.Context, code string, internship *internship.Internship) error } type discountService struct { @@ -144,3 +148,65 @@ func (s *discountService) GetByCode(ctx context.Context, code string) (*dto.Disc return &dto.DiscountResponse{Discount: *discount}, nil } + +func (s *discountService) ValidateDiscountCode(ctx context.Context, code string, internship *internship.Internship) error { + if internship == nil { + return ierr.NewError("internship not found"). + WithHint("Internship not found"). + Mark(ierr.ErrNotFound) + } + + discount, err := s.ServiceParams.DiscountRepo.GetByCode(ctx, code) + if err != nil { + return err + } + + if discount == nil { + return ierr.NewError("discount not found"). + WithHint("Discount not found"). + Mark(ierr.ErrNotFound) + } + + // Check if discount is active + if !discount.IsActive || discount.Status != types.StatusPublished { + return ierr.NewError("discount is not active"). + WithHint("Discount is not active"). + Mark(ierr.ErrBadRequest) + } + + // Check if discount is within valid time range + now := time.Now() + if discount.ValidFrom.After(now) { + return ierr.NewError("discount is not yet valid"). + WithHint("Discount is not yet valid"). + Mark(ierr.ErrBadRequest) + } + + if discount.ValidUntil != nil && discount.ValidUntil.Before(now) { + return ierr.NewError("discount has expired"). + WithHint("Discount has expired"). + Mark(ierr.ErrBadRequest) + } + + // Check minimum order value requirement + if discount.MinOrderValue != nil && discount.MinOrderValue.GreaterThan(decimal.Zero) { + if discount.MinOrderValue.GreaterThan(internship.Price) { + return ierr.NewError("order value does not meet minimum requirement for discount"). + WithHint("Order value does not meet minimum requirement for discount"). + Mark(ierr.ErrBadRequest) + } + } + + // TODO: Implement max uses validation by counting actual usage from payments/enrollments + // For now, we'll skip this validation to avoid the UsedCount error + // if discount.MaxUses != nil && *discount.MaxUses > 0 { + // actualUsage := s.getDiscountUsageCount(ctx, discount.ID) + // if actualUsage >= *discount.MaxUses { + // return ierr.NewError("discount has reached the maximum number of uses"). + // WithHint("Discount has reached the maximum number of uses"). + // Mark(ierr.ErrBadRequest) + // } + // } + + return nil +} diff --git a/internal/service/factory.go b/internal/service/factory.go index 4d6b35e..6b0dafc 100644 --- a/internal/service/factory.go +++ b/internal/service/factory.go @@ -3,6 +3,9 @@ package service import ( "github.com/omkar273/codegeeky/internal/config" "github.com/omkar273/codegeeky/internal/domain/discount" + "github.com/omkar273/codegeeky/internal/domain/internship" + "github.com/omkar273/codegeeky/internal/domain/internshipenrollment" + "github.com/omkar273/codegeeky/internal/domain/payment" "github.com/omkar273/codegeeky/internal/domain/user" "github.com/omkar273/codegeeky/internal/httpclient" "github.com/omkar273/codegeeky/internal/logger" @@ -20,8 +23,13 @@ type ServiceParams struct { DB postgres.IClient // Repository dependencies - UserRepo user.Repository - DiscountRepo discount.Repository + UserRepo user.Repository + DiscountRepo discount.Repository + PaymentRepo payment.Repository + InternshipRepo internship.InternshipRepository + InternshipBatchRepo internship.InternshipBatchRepository + CategoryRepo internship.CategoryRepository + InternshipEnrollmentRepo internshipenrollment.Repository // Service dependencies WebhookPublisher publisher.WebhookPublisher diff --git a/internal/service/internship.go b/internal/service/internship.go index e928e56..1b17863 100644 --- a/internal/service/internship.go +++ b/internal/service/internship.go @@ -4,9 +4,7 @@ import ( "context" "github.com/omkar273/codegeeky/internal/api/dto" - domainInternship "github.com/omkar273/codegeeky/internal/domain/internship" ierr "github.com/omkar273/codegeeky/internal/errors" - "github.com/omkar273/codegeeky/internal/logger" "github.com/omkar273/codegeeky/internal/types" "github.com/samber/lo" ) @@ -20,17 +18,14 @@ type InternshipService interface { } type internshipService struct { - internshipRepo domainInternship.InternshipRepository - logger *logger.Logger + ServiceParams } func NewInternshipService( - internshipRepo domainInternship.InternshipRepository, - logger *logger.Logger, + params ServiceParams, ) InternshipService { return &internshipService{ - internshipRepo: internshipRepo, - logger: logger, + ServiceParams: params, } } @@ -42,7 +37,7 @@ func (s *internshipService) Create(ctx context.Context, req *dto.CreateInternshi internship := req.ToInternship(ctx) - err := s.internshipRepo.Create(ctx, internship) + err := s.InternshipRepo.Create(ctx, internship) if err != nil { return nil, err } @@ -54,7 +49,7 @@ func (s *internshipService) Create(ctx context.Context, req *dto.CreateInternshi func (s *internshipService) GetByID(ctx context.Context, id string) (*dto.InternshipResponse, error) { - internship, err := s.internshipRepo.Get(ctx, id) + internship, err := s.InternshipRepo.Get(ctx, id) if err != nil { return nil, err } @@ -74,7 +69,7 @@ func (s *internshipService) Update(ctx context.Context, id string, req *dto.Upda return nil, err } - existingInternship, err := s.internshipRepo.Get(ctx, id) + existingInternship, err := s.InternshipRepo.Get(ctx, id) if err != nil { return nil, err } @@ -101,10 +96,10 @@ func (s *internshipService) Update(ctx context.Context, id string, req *dto.Upda existingInternship.Price = lo.FromPtr(req.Price) } if req.FlatDiscount != nil { - existingInternship.FlatDiscount = lo.FromPtr(req.FlatDiscount) + existingInternship.FlatDiscount = req.FlatDiscount } if req.PercentageDiscount != nil { - existingInternship.PercentageDiscount = lo.FromPtr(req.PercentageDiscount) + existingInternship.PercentageDiscount = req.PercentageDiscount } if req.Skills != nil { existingInternship.Skills = req.Skills @@ -119,7 +114,7 @@ func (s *internshipService) Update(ctx context.Context, id string, req *dto.Upda existingInternship.Benefits = req.Benefits } - err = s.internshipRepo.Update(ctx, existingInternship) + err = s.InternshipRepo.Update(ctx, existingInternship) if err != nil { return nil, err } @@ -131,12 +126,12 @@ func (s *internshipService) Update(ctx context.Context, id string, req *dto.Upda func (s *internshipService) Delete(ctx context.Context, id string) error { - _, err := s.internshipRepo.Get(ctx, id) + _, err := s.InternshipRepo.Get(ctx, id) if err != nil { return err } - err = s.internshipRepo.Delete(ctx, id) + err = s.InternshipRepo.Delete(ctx, id) if err != nil { return err } @@ -154,12 +149,12 @@ func (s *internshipService) List(ctx context.Context, filter *types.InternshipFi return nil, err } - count, err := s.internshipRepo.Count(ctx, filter) + count, err := s.InternshipRepo.Count(ctx, filter) if err != nil { return nil, err } - internships, err := s.internshipRepo.List(ctx, filter) + internships, err := s.InternshipRepo.List(ctx, filter) if err != nil { return nil, err } diff --git a/internal/service/internship_batch.go b/internal/service/internship_batch.go new file mode 100644 index 0000000..1b16535 --- /dev/null +++ b/internal/service/internship_batch.go @@ -0,0 +1,138 @@ +package service + +import ( + "context" + + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +type InternshipBatchService interface { + Create(ctx context.Context, req *dto.CreateInternshipBatchRequest) (*dto.InternshipBatchResponse, error) + Get(ctx context.Context, id string) (*dto.InternshipBatchResponse, error) + Update(ctx context.Context, id string, req *dto.UpdateInternshipBatchRequest) (*dto.InternshipBatchResponse, error) + Delete(ctx context.Context, id string) error + List(ctx context.Context, filter *types.InternshipBatchFilter) (*dto.ListInternshipBatchResponse, error) +} + +type internshipBatchService struct { + ServiceParams +} + +func NewInternshipBatchService( + params ServiceParams, +) InternshipBatchService { + return &internshipBatchService{ + ServiceParams: params, + } +} + +func (s *internshipBatchService) Create(ctx context.Context, req *dto.CreateInternshipBatchRequest) (*dto.InternshipBatchResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + batch := req.ToInternshipBatch(ctx) + + err := s.InternshipBatchRepo.Create(ctx, batch) + if err != nil { + return nil, err + } + + return &dto.InternshipBatchResponse{ + InternshipBatch: *batch, + }, nil +} + +func (s *internshipBatchService) Get(ctx context.Context, id string) (*dto.InternshipBatchResponse, error) { + batch, err := s.InternshipBatchRepo.Get(ctx, id) + if err != nil { + return nil, err + } + + return &dto.InternshipBatchResponse{ + InternshipBatch: *batch, + }, nil +} + +func (s *internshipBatchService) Update(ctx context.Context, id string, req *dto.UpdateInternshipBatchRequest) (*dto.InternshipBatchResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + existingBatch, err := s.InternshipBatchRepo.Get(ctx, id) + if err != nil { + return nil, err + } + + if req.Name != nil { + existingBatch.Name = lo.FromPtr(req.Name) + } + if req.Description != nil { + existingBatch.Description = lo.FromPtr(req.Description) + } + if req.StartDate != nil { + existingBatch.StartDate = lo.FromPtr(req.StartDate) + } + if req.EndDate != nil { + existingBatch.EndDate = lo.FromPtr(req.EndDate) + } + if req.BatchStatus != nil { + existingBatch.BatchStatus = lo.FromPtr(req.BatchStatus) + } + + err = s.InternshipBatchRepo.Update(ctx, existingBatch) + if err != nil { + return nil, err + } + + return &dto.InternshipBatchResponse{ + InternshipBatch: *existingBatch, + }, nil +} + +func (s *internshipBatchService) Delete(ctx context.Context, id string) error { + _, err := s.InternshipBatchRepo.Get(ctx, id) + if err != nil { + return err + } + + err = s.InternshipBatchRepo.Delete(ctx, id) + if err != nil { + return err + } + + return nil +} + +func (s *internshipBatchService) List(ctx context.Context, filter *types.InternshipBatchFilter) (*dto.ListInternshipBatchResponse, error) { + if filter == nil { + filter = types.NewInternshipBatchFilter() + } + + if err := filter.Validate(); err != nil { + return nil, err + } + + count, err := s.InternshipBatchRepo.Count(ctx, filter) + if err != nil { + return nil, err + } + + batches, err := s.InternshipBatchRepo.List(ctx, filter) + if err != nil { + return nil, err + } + + response := &dto.ListInternshipBatchResponse{ + Items: make([]*dto.InternshipBatchResponse, count), + Pagination: types.NewPaginationResponse(count, filter.GetLimit(), filter.GetOffset()), + } + + for i, batch := range batches { + response.Items[i] = &dto.InternshipBatchResponse{InternshipBatch: *batch} + } + + return response, nil +} diff --git a/internal/service/internship_enrollment.go b/internal/service/internship_enrollment.go new file mode 100644 index 0000000..0fd6f2d --- /dev/null +++ b/internal/service/internship_enrollment.go @@ -0,0 +1,109 @@ +package service + +import ( + "context" + + "github.com/omkar273/codegeeky/internal/api/dto" + domainInternshipEnrollment "github.com/omkar273/codegeeky/internal/domain/internshipenrollment" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/idempotency" + "github.com/omkar273/codegeeky/internal/types" +) + +// InternshipEnrollmentService is the service for managing internship enrollments +type InternshipEnrollmentService interface { + InitializeEnrollment(ctx context.Context, req *dto.InitializeEnrollmentRequest) (*dto.InitializeEnrollmentResponse, error) +} + +// internshipEnrollmentService is the implementation of the InternshipEnrollmentService interface +type internshipEnrollmentService struct { + ServiceParams + PricingService PricingService +} + +// NewInternshipEnrollmentService creates a new InternshipEnrollmentService +func NewInternshipEnrollmentService(params ServiceParams, pricingService PricingService) InternshipEnrollmentService { + return &internshipEnrollmentService{ + ServiceParams: params, + PricingService: pricingService, + } +} + +// InitializeEnrollment initializes an internship enrollment +func (s *internshipEnrollmentService) InitializeEnrollment(ctx context.Context, req *dto.InitializeEnrollmentRequest) (*dto.InitializeEnrollmentResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + batch, err := s.ServiceParams.InternshipBatchRepo.Get(ctx, req.InternshipBatchID) + if err != nil { + return nil, err + } + + // Generate idempotency key + generator := idempotency.NewGenerator() + idempotencyKey := generator.GenerateKey(idempotency.ScopeInternship, map[string]interface{}{ + "user_id": types.GetUserID(ctx), + "internship_id": batch.InternshipID, + "batch_id": batch.ID, + }) + + // Check for existing enrollment + existingEnrollment, err := s.ServiceParams.InternshipEnrollmentRepo.GetByIdempotencyKey(ctx, idempotencyKey) + if err != nil && !ierr.IsNotFound(err) { + return nil, err + } + + if existingEnrollment != nil { + if existingEnrollment.EnrollmentStatus == types.InternshipEnrollmentStatusCompleted { + return nil, ierr.NewError("enrollment already completed"). + WithHint("Enrollment already completed"). + Mark(ierr.ErrAlreadyExists) + } + + // Return existing enrollment if still pending + return &dto.InitializeEnrollmentResponse{ + EnrollmentID: existingEnrollment.ID, + EnrollmentStatus: existingEnrollment.EnrollmentStatus, + PaymentRequired: existingEnrollment.PaymentStatus == types.PaymentStatusPending, + }, nil + } + + // Calculate pricing with coupon (use first coupon code if multiple provided) + var discountCode string + if len(req.CouponCodes) > 0 { + discountCode = req.CouponCodes[0] + } + + pricingResponse, err := s.PricingService.CalculateEnrollmentPricing(ctx, req.InternshipBatchID, []string{discountCode}) + if err != nil { + return nil, err + } + + // Create enrollment + enrollmentData := &domainInternshipEnrollment.InternshipEnrollment{ + UserID: types.GetUserID(ctx), + InternshipID: batch.InternshipID, + EnrollmentStatus: types.InternshipEnrollmentStatusPending, + PaymentStatus: types.PaymentStatusPending, + IdempotencyKey: &idempotencyKey, + Metadata: req.Metadata, + } + + // If payment is not required, mark as completed + if !pricingResponse.PaymentRequired { + enrollmentData.EnrollmentStatus = types.InternshipEnrollmentStatusCompleted + enrollmentData.PaymentStatus = types.PaymentStatusSuccess + } + + err = s.ServiceParams.InternshipEnrollmentRepo.Create(ctx, enrollmentData) + if err != nil { + return nil, err + } + + return &dto.InitializeEnrollmentResponse{ + EnrollmentID: enrollmentData.ID, + EnrollmentStatus: enrollmentData.EnrollmentStatus, + PaymentRequired: pricingResponse.PaymentRequired, + }, nil +} diff --git a/internal/service/payment.go b/internal/service/payment.go new file mode 100644 index 0000000..a359ee5 --- /dev/null +++ b/internal/service/payment.go @@ -0,0 +1,338 @@ +package service + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/api/dto" + domainPayment "github.com/omkar273/codegeeky/internal/domain/payment" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// PaymentService defines the interface for payment operations +type PaymentService interface { + // Core payment CRUD operations + Create(ctx context.Context, req *dto.CreatePaymentRequest) (*dto.PaymentResponse, error) + GetByID(ctx context.Context, id string) (*dto.PaymentResponse, error) + GetByIdempotencyKey(ctx context.Context, key string) (*dto.PaymentResponse, error) + Update(ctx context.Context, id string, req *dto.UpdatePaymentRequest) (*dto.PaymentResponse, error) + Delete(ctx context.Context, id string) error + List(ctx context.Context, filter *types.PaymentFilter) (*dto.ListPaymentResponse, error) + + // Payment attempt operations + CreateAttempt(ctx context.Context, req *dto.PaymentAttemptRequest) (*dto.PaymentAttemptResponse, error) + GetAttempt(ctx context.Context, id string) (*dto.PaymentAttemptResponse, error) + ListAttempts(ctx context.Context, paymentID string) ([]*dto.PaymentAttemptResponse, error) + GetLatestAttempt(ctx context.Context, paymentID string) (*dto.PaymentAttemptResponse, error) + + // Status management + UpdateStatus(ctx context.Context, paymentID string, status types.PaymentStatus, metadata map[string]string) (*dto.PaymentResponse, error) + MarkAsSuccess(ctx context.Context, paymentID string, gatewayPaymentID *string, metadata map[string]string) (*dto.PaymentResponse, error) + MarkAsFailed(ctx context.Context, paymentID string, errorMessage string, metadata map[string]string) (*dto.PaymentResponse, error) + MarkAsRefunded(ctx context.Context, paymentID string, metadata map[string]string) (*dto.PaymentResponse, error) +} + +type paymentService struct { + ServiceParams ServiceParams +} + +// NewPaymentService creates a new payment service instance +func NewPaymentService(params ServiceParams) PaymentService { + return &paymentService{ + ServiceParams: params, + } +} + +// Create creates a new payment record +func (s *paymentService) Create(ctx context.Context, req *dto.CreatePaymentRequest) (*dto.PaymentResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + // Check for existing payment with same idempotency key + if req.IdempotencyKey != "" { + existingPayment, err := s.ServiceParams.PaymentRepo.GetByIdempotencyKey(ctx, req.IdempotencyKey) + if err != nil && !ierr.IsNotFound(err) { + return nil, err + } + if existingPayment != nil { + return &dto.PaymentResponse{ + Payment: *existingPayment, + }, nil + } + } + + var createdPayment *domainPayment.Payment + + // Create payment in transaction + err := s.ServiceParams.DB.WithTx(ctx, func(ctx context.Context) error { + // Convert request to domain model + payment := req.ToPayment(ctx) + + // Create payment in database + if err := s.ServiceParams.PaymentRepo.Create(ctx, payment); err != nil { + return err + } + + // If tracking attempts, create initial attempt + if payment.TrackAttempts { + attemptReq := &dto.PaymentAttemptRequest{ + PaymentID: payment.ID, + PaymentStatus: types.PaymentStatusPending, + Metadata: types.MetadataFromEnt(payment.Metadata), + } + + attempt := attemptReq.ToPaymentAttempt(ctx, 1) + if err := s.ServiceParams.PaymentRepo.CreateAttempt(ctx, attempt); err != nil { + s.ServiceParams.Logger.Errorw("failed to create initial payment attempt", + "payment_id", payment.ID, "error", err) + // Don't fail the payment creation for attempt creation failure + } + } + + createdPayment = payment + return nil + }) + + if err != nil { + return nil, err + } + + return &dto.PaymentResponse{ + Payment: *createdPayment, + }, nil +} + +// GetByID retrieves a payment by its ID +func (s *paymentService) GetByID(ctx context.Context, id string) (*dto.PaymentResponse, error) { + payment, err := s.ServiceParams.PaymentRepo.Get(ctx, id) + if err != nil { + return nil, err + } + + return &dto.PaymentResponse{ + Payment: *payment, + }, nil +} + +// GetByIdempotencyKey retrieves a payment by its idempotency key +func (s *paymentService) GetByIdempotencyKey(ctx context.Context, key string) (*dto.PaymentResponse, error) { + payment, err := s.ServiceParams.PaymentRepo.GetByIdempotencyKey(ctx, key) + if err != nil { + return nil, err + } + + return &dto.PaymentResponse{ + Payment: *payment, + }, nil +} + +// Update updates an existing payment +func (s *paymentService) Update(ctx context.Context, id string, req *dto.UpdatePaymentRequest) (*dto.PaymentResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + var updatedPayment *domainPayment.Payment + + err := s.ServiceParams.DB.WithTx(ctx, func(ctx context.Context) error { + // Get existing payment + existingPayment, err := s.ServiceParams.PaymentRepo.Get(ctx, id) + if err != nil { + return err + } + + // Update fields + if req.PaymentStatus != nil { + existingPayment.PaymentStatus = *req.PaymentStatus + + // Set status timestamps + now := time.Now() + switch *req.PaymentStatus { + case types.PaymentStatusSuccess: + existingPayment.SucceededAt = &now + case types.PaymentStatusFailed: + existingPayment.FailedAt = &now + case types.PaymentStatusRefunded: + existingPayment.RefundedAt = &now + } + } + + if req.GatewayPaymentID != nil { + existingPayment.GatewayPaymentID = req.GatewayPaymentID + } + + if req.ErrorMessage != nil { + existingPayment.ErrorMessage = req.ErrorMessage + } + + if req.Metadata != nil { + existingPayment.Metadata = req.Metadata + } + + // Update in database + if err := s.ServiceParams.PaymentRepo.Update(ctx, existingPayment); err != nil { + return err + } + + updatedPayment = existingPayment + return nil + }) + + if err != nil { + return nil, err + } + + return &dto.PaymentResponse{ + Payment: *updatedPayment, + }, nil +} + +// Delete deletes a payment by its ID +func (s *paymentService) Delete(ctx context.Context, id string) error { + // Verify payment exists + _, err := s.ServiceParams.PaymentRepo.Get(ctx, id) + if err != nil { + return err + } + + return s.ServiceParams.PaymentRepo.Delete(ctx, id) +} + +// List retrieves a paginated list of payments +func (s *paymentService) List(ctx context.Context, filter *types.PaymentFilter) (*dto.ListPaymentResponse, error) { + if filter == nil { + filter = types.NewNoLimitPaymentFilter() + } + + if err := filter.Validate(); err != nil { + return nil, err + } + + count, err := s.ServiceParams.PaymentRepo.Count(ctx, filter) + if err != nil { + return nil, err + } + + payments, err := s.ServiceParams.PaymentRepo.List(ctx, filter) + if err != nil { + return nil, err + } + + response := &dto.ListPaymentResponse{ + Items: make([]*dto.PaymentResponse, len(payments)), + Pagination: lo.ToPtr(types.NewPaginationResponse(count, filter.GetLimit(), filter.GetOffset())), + } + + for i, payment := range payments { + response.Items[i] = &dto.PaymentResponse{Payment: *payment} + } + + return response, nil +} + +// CreateAttempt creates a new payment attempt +func (s *paymentService) CreateAttempt(ctx context.Context, req *dto.PaymentAttemptRequest) (*dto.PaymentAttemptResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + // Get existing attempts to determine attempt number + attempts, err := s.ServiceParams.PaymentRepo.ListAttempts(ctx, req.PaymentID) + if err != nil && !ierr.IsNotFound(err) { + return nil, err + } + + attemptNumber := len(attempts) + 1 + attempt := req.ToPaymentAttempt(ctx, attemptNumber) + + if err := s.ServiceParams.PaymentRepo.CreateAttempt(ctx, attempt); err != nil { + return nil, err + } + + return &dto.PaymentAttemptResponse{ + Attempt: *attempt, + }, nil +} + +// GetAttempt retrieves a payment attempt by its ID +func (s *paymentService) GetAttempt(ctx context.Context, id string) (*dto.PaymentAttemptResponse, error) { + attempt, err := s.ServiceParams.PaymentRepo.GetAttempt(ctx, id) + if err != nil { + return nil, err + } + + return &dto.PaymentAttemptResponse{ + Attempt: *attempt, + }, nil +} + +// ListAttempts retrieves all attempts for a payment +func (s *paymentService) ListAttempts(ctx context.Context, paymentID string) ([]*dto.PaymentAttemptResponse, error) { + attempts, err := s.ServiceParams.PaymentRepo.ListAttempts(ctx, paymentID) + if err != nil { + return nil, err + } + + responses := make([]*dto.PaymentAttemptResponse, len(attempts)) + for i, attempt := range attempts { + responses[i] = &dto.PaymentAttemptResponse{Attempt: *attempt} + } + + return responses, nil +} + +// GetLatestAttempt retrieves the latest attempt for a payment +func (s *paymentService) GetLatestAttempt(ctx context.Context, paymentID string) (*dto.PaymentAttemptResponse, error) { + attempt, err := s.ServiceParams.PaymentRepo.GetLatestAttempt(ctx, paymentID) + if err != nil { + return nil, err + } + + return &dto.PaymentAttemptResponse{ + Attempt: *attempt, + }, nil +} + +// UpdateStatus updates the payment status with optional metadata +func (s *paymentService) UpdateStatus(ctx context.Context, paymentID string, status types.PaymentStatus, metadata map[string]string) (*dto.PaymentResponse, error) { + req := &dto.UpdatePaymentRequest{ + PaymentStatus: &status, + Metadata: metadata, + } + return s.Update(ctx, paymentID, req) +} + +// MarkAsSuccess marks a payment as successful +func (s *paymentService) MarkAsSuccess(ctx context.Context, paymentID string, gatewayPaymentID *string, metadata map[string]string) (*dto.PaymentResponse, error) { + status := types.PaymentStatusSuccess + req := &dto.UpdatePaymentRequest{ + PaymentStatus: &status, + GatewayPaymentID: gatewayPaymentID, + Metadata: metadata, + } + return s.Update(ctx, paymentID, req) +} + +// MarkAsFailed marks a payment as failed +func (s *paymentService) MarkAsFailed(ctx context.Context, paymentID string, errorMessage string, metadata map[string]string) (*dto.PaymentResponse, error) { + status := types.PaymentStatusFailed + req := &dto.UpdatePaymentRequest{ + PaymentStatus: &status, + ErrorMessage: &errorMessage, + Metadata: metadata, + } + return s.Update(ctx, paymentID, req) +} + +// MarkAsRefunded marks a payment as refunded +func (s *paymentService) MarkAsRefunded(ctx context.Context, paymentID string, metadata map[string]string) (*dto.PaymentResponse, error) { + status := types.PaymentStatusRefunded + req := &dto.UpdatePaymentRequest{ + PaymentStatus: &status, + Metadata: metadata, + } + return s.Update(ctx, paymentID, req) +} diff --git a/internal/service/pricing.go b/internal/service/pricing.go new file mode 100644 index 0000000..0e23e6d --- /dev/null +++ b/internal/service/pricing.go @@ -0,0 +1,142 @@ +package service + +import ( + "context" + "fmt" + + "github.com/omkar273/codegeeky/internal/api/dto" + "github.com/omkar273/codegeeky/internal/domain/discount" + "github.com/omkar273/codegeeky/internal/domain/internship" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/shopspring/decimal" +) + +// PricingService is the service for calculating pricing for an internship +type PricingService interface { + CalculateEnrollmentPricing(ctx context.Context, internshipID string, discountCodes []string) (*dto.PricingResponse, error) +} + +// pricingService is the implementation of the PricingService interface +type pricingService struct { + ServiceParams +} + +// NewPricingService creates a new PricingService +func NewPricingService(params ServiceParams) PricingService { + return &pricingService{ + ServiceParams: params, + } +} + +// CalculateEnrollmentPricing calculates the pricing for an internship enrollment +func (s *pricingService) CalculateEnrollmentPricing(ctx context.Context, internshipID string, discountCodes []string) (*dto.PricingResponse, error) { + // Validate input parameters + if internshipID == "" { + return nil, ierr.NewError("internship ID is required"). + WithHint("Please provide a valid internship ID"). + Mark(ierr.ErrValidation) + } + + // Get the internship + internship, err := s.ServiceParams.InternshipRepo.Get(ctx, internshipID) + if err != nil { + return nil, fmt.Errorf("failed to get internship: %w", err) + } + + // Validate and get discounts + var discounts []*discount.Discount + if len(discountCodes) > 0 { + discounts, err = s.getValidDiscounts(ctx, discountCodes, internship) + if err != nil { + return nil, err + } + } + + // Calculate pricing with discounts + pricing := s.calculatePricingWithDiscounts(internship, discounts) + + return pricing, nil +} + +// getValidDiscounts validates discount codes and retrieves discount details +func (s *pricingService) getValidDiscounts(ctx context.Context, discountCodes []string, internship *internship.Internship) ([]*discount.Discount, error) { + // Validate each discount code + discountService := NewDiscountService(s.ServiceParams) + for _, code := range discountCodes { + if err := discountService.ValidateDiscountCode(ctx, code, internship); err != nil { + return nil, fmt.Errorf("invalid discount code '%s': %w", code, err) + } + } + + // Get all valid discounts in a single query + discountsResponse, err := discountService.List(ctx, &types.DiscountFilter{ + Codes: discountCodes, + }) + if err != nil { + return nil, fmt.Errorf("failed to retrieve discounts: %w", err) + } + + // Convert DTO responses to domain discount objects + discounts := make([]*discount.Discount, len(discountsResponse.Items)) + for i, discountResponse := range discountsResponse.Items { + discounts[i] = &discountResponse.Discount + } + + return discounts, nil +} + +// calculatePricingWithDiscounts computes the final pricing with applied discounts +func (s *pricingService) calculatePricingWithDiscounts(internship *internship.Internship, discounts []*discount.Discount) *dto.PricingResponse { + // Initialize pricing calculation + total := internship.Total + discountAmount := decimal.Zero + discountsApplied := []*dto.DiscountInfo{} + + // Apply each discount + for _, discount := range discounts { + var discountValue decimal.Decimal + + switch discount.DiscountType { + case types.DiscountTypeFlat: + // Ensure flat discount doesn't exceed the current total + discountValue = decimal.Min(discount.DiscountValue, total) + + case types.DiscountTypePercentage: + // Calculate percentage discount on current total + percentageDiscount := total.Mul(discount.DiscountValue).Div(decimal.NewFromInt(100)) + discountValue = decimal.Min(percentageDiscount, total) + } + discountAmount = discountAmount.Add(discountValue) + total = total.Sub(discountValue) + + // Ensure total never goes below zero + if total.LessThan(decimal.Zero) { + total = decimal.Zero + } + + // Add discount info to applied discounts list + discountsApplied = append(discountsApplied, &dto.DiscountInfo{ + Code: discount.Code, + Amount: discountValue, + Description: discount.Description, + }) + + } + + // Calculate savings percentage + savingsPercent := decimal.Zero + if internship.Subtotal.GreaterThan(decimal.Zero) { + savingsPercent = discountAmount.Div(internship.Subtotal).Mul(decimal.NewFromInt(100)) + } + + return &dto.PricingResponse{ + InternshipID: internship.ID, + Subtotal: internship.Subtotal, + Total: total, + DiscountAmount: discountAmount, + AppliedDiscounts: discountsApplied, + PaymentRequired: total.GreaterThan(decimal.Zero), + SavingsPercent: savingsPercent.InexactFloat64(), + } +} diff --git a/internal/service/user.go b/internal/service/user.go index 4877887..77a1907 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -4,7 +4,6 @@ import ( "context" "github.com/omkar273/codegeeky/internal/api/dto" - "github.com/omkar273/codegeeky/internal/domain/user" ierr "github.com/omkar273/codegeeky/internal/errors" "github.com/omkar273/codegeeky/internal/types" ) @@ -15,12 +14,12 @@ type UserService interface { } type userService struct { - userRepository user.Repository + ServiceParams } // NewUserService creates a new user service -func NewUserService(userRepository user.Repository) UserService { - return &userService{userRepository: userRepository} +func NewUserService(params ServiceParams) UserService { + return &userService{ServiceParams: params} } // Me returns the current user @@ -33,7 +32,7 @@ func (s *userService) Me(ctx context.Context) (*dto.MeResponse, error) { Mark(ierr.ErrPermissionDenied) } - user, err := s.userRepository.Get(ctx, userID) + user, err := s.UserRepo.Get(ctx, userID) if err != nil { return nil, ierr.WithError(err). WithHint("Failed to get user"). @@ -58,7 +57,7 @@ func (s *userService) Update(ctx context.Context, req *dto.UpdateUserRequest) (* Mark(ierr.ErrPermissionDenied) } - user, err := s.userRepository.Get(ctx, userID) + user, err := s.UserRepo.Get(ctx, userID) if err != nil { return nil, ierr.WithError(err). WithHint("Failed to get user"). @@ -72,7 +71,7 @@ func (s *userService) Update(ctx context.Context, req *dto.UpdateUserRequest) (* user.Phone = req.Phone } - err = s.userRepository.Update(ctx, user) + err = s.UserRepo.Update(ctx, user) if err != nil { return nil, err } diff --git a/internal/testutil/base_service_suite.go b/internal/testutil/base_service_suite.go new file mode 100644 index 0000000..09e9c2a --- /dev/null +++ b/internal/testutil/base_service_suite.go @@ -0,0 +1,145 @@ +package testutil + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/cache" + "github.com/omkar273/codegeeky/internal/config" + "github.com/omkar273/codegeeky/internal/domain/cart" + "github.com/omkar273/codegeeky/internal/domain/discount" + "github.com/omkar273/codegeeky/internal/domain/internship" + "github.com/omkar273/codegeeky/internal/domain/internshipenrollment" + "github.com/omkar273/codegeeky/internal/domain/user" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/postgres" + "github.com/omkar273/codegeeky/internal/types" + "github.com/omkar273/codegeeky/internal/validator" + "github.com/stretchr/testify/suite" +) + +// Stores holds all the repository interfaces for testing +type Stores struct { + UserRepo user.Repository + CartRepo cart.Repository + DiscountRepo discount.Repository + InternshipRepo internship.InternshipRepository + InternshipBatchRepo internship.InternshipBatchRepository + InternshipEnrollmentRepo internshipenrollment.Repository +} + +// BaseServiceTestSuite provides common functionality for all service test suites +type BaseServiceTestSuite struct { + suite.Suite + ctx context.Context + stores Stores + db postgres.IClient + logger *logger.Logger + config *config.Configuration + now time.Time +} + +// SetupSuite is called once before running the tests in the suite +func (s *BaseServiceTestSuite) SetupSuite() { + // Initialize validator + validator.NewValidator() + + // Initialize logger with test config + cfg := &config.Configuration{ + Logging: config.LoggingConfig{ + Level: types.LogLevelDebug, + }, + } + var err error + s.config = cfg + s.logger, err = logger.NewLogger(cfg) + if err != nil { + s.T().Fatalf("failed to create logger: %v", err) + } + + // Initialize cache + cache.Initialize(s.logger) +} + +// SetupTest is called before each test +func (s *BaseServiceTestSuite) SetupTest() { + s.setupContext() + s.setupStores() + s.now = time.Now().UTC() +} + +// TearDownTest is called after each test +func (s *BaseServiceTestSuite) TearDownTest() { + s.clearStores() +} + +func (s *BaseServiceTestSuite) setupContext() { + s.ctx = context.Background() + s.ctx = context.WithValue(s.ctx, types.CtxUserID, types.DefaultUserID) + s.ctx = context.WithValue(s.ctx, types.CtxRequestID, types.GenerateUUID()) + s.ctx = context.WithValue(s.ctx, types.CtxUserEmail, types.DefaultUserEmail) + s.ctx = context.WithValue(s.ctx, types.CtxUserRole, types.DefaultUserRole) + s.ctx = context.WithValue(s.ctx, types.CtxUser, types.DefaultUserID) + s.ctx = context.WithValue(s.ctx, types.CtxIsGuest, types.DefaultIsGuest) +} + +func (s *BaseServiceTestSuite) setupStores() { + s.stores = Stores{ + UserRepo: NewInMemoryUserStore(), + CartRepo: NewInMemoryCartStore(), + DiscountRepo: NewInMemoryDiscountStore(), + InternshipRepo: NewInMemoryInternshipStore(), + InternshipBatchRepo: NewInMemoryInternshipBatchStore(), + InternshipEnrollmentRepo: NewInMemoryInternshipEnrollmentStore(), + } + + s.db = NewMockPostgresClient(s.logger) +} + +func (s *BaseServiceTestSuite) clearStores() { + s.stores.CartRepo.(*InMemoryCartStore).Clear() + s.stores.UserRepo.(*InMemoryUserStore).Clear() + s.stores.DiscountRepo.(*InMemoryDiscountStore).Clear() + s.stores.InternshipRepo.(*InMemoryInternshipStore).Clear() + s.stores.InternshipBatchRepo.(*InMemoryInternshipBatchStore).Clear() + s.stores.InternshipEnrollmentRepo.(*InMemoryInternshipEnrollmentStore).Clear() +} + +func (s *BaseServiceTestSuite) ClearStores() { + s.clearStores() +} + +// GetContext returns the test context +func (s *BaseServiceTestSuite) GetContext() context.Context { + return s.ctx +} + +// GetConfig returns the test configuration +func (s *BaseServiceTestSuite) GetConfig() *config.Configuration { + return s.config +} + +// GetStores returns all test repositories +func (s *BaseServiceTestSuite) GetStores() Stores { + return s.stores +} + +// GetDB returns the test database client +func (s *BaseServiceTestSuite) GetDB() postgres.IClient { + return s.db +} + +// GetLogger returns the test logger +func (s *BaseServiceTestSuite) GetLogger() *logger.Logger { + return s.logger +} + +// GetNow returns the current test time +func (s *BaseServiceTestSuite) GetNow() time.Time { + return s.now.UTC() +} + +// GetUUID returns a new UUID string +func (s *BaseServiceTestSuite) GetUUID() string { + return types.GenerateUUID() +} diff --git a/internal/testutil/context.go b/internal/testutil/context.go new file mode 100644 index 0000000..4476c48 --- /dev/null +++ b/internal/testutil/context.go @@ -0,0 +1,27 @@ +package testutil + +import ( + "context" + + "github.com/omkar273/codegeeky/internal/types" +) + +// CtxRequestID ContextKey = "ctx_request_id" +// +// CtxUserID ContextKey = "ctx_user_id" +// CtxJWT ContextKey = "ctx_jwt" +// CtxUserEmail ContextKey = "ctx_user_email" +// CtxDBTransaction ContextKey = "ctx_db_transaction" +// CtxUserRole ContextKey = "ctx_user_role" +// CtxAuthContext ContextKey = "ctx_auth_context" +// CtxUser ContextKey = "ctx_user" +func SetupContext() context.Context { + ctx := context.Background() + ctx = context.WithValue(ctx, types.CtxUserID, types.DefaultUserID) + ctx = context.WithValue(ctx, types.CtxRequestID, types.GenerateUUID()) + ctx = context.WithValue(ctx, types.CtxUserEmail, types.DefaultUserEmail) + ctx = context.WithValue(ctx, types.CtxUserRole, types.DefaultUserRole) + ctx = context.WithValue(ctx, types.CtxUser, types.DefaultUserID) + + return ctx +} diff --git a/internal/testutil/inmemory_cart_store.go b/internal/testutil/inmemory_cart_store.go new file mode 100644 index 0000000..e9314c3 --- /dev/null +++ b/internal/testutil/inmemory_cart_store.go @@ -0,0 +1,386 @@ +package testutil + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/domain/cart" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// InMemoryCartStore implements cart.Repository +type InMemoryCartStore struct { + *InMemoryStore[*cart.Cart] + lineItems *InMemoryStore[*cart.CartLineItem] +} + +// NewInMemoryCartStore creates a new in-memory cart store +func NewInMemoryCartStore() *InMemoryCartStore { + return &InMemoryCartStore{ + InMemoryStore: NewInMemoryStore[*cart.Cart](), + lineItems: NewInMemoryStore[*cart.CartLineItem](), + } +} + +// cartFilterFn implements filtering logic for carts +func cartFilterFn(ctx context.Context, c *cart.Cart, filter interface{}) bool { + if c == nil { + return false + } + + filter_, ok := filter.(*types.CartFilter) + if !ok { + return true // No filter applied + } + + // Filter by user ID + if filter_.UserID != "" { + if c.UserID != filter_.UserID { + return false + } + } + + // Filter by cart type + if filter_.CartType != nil { + if c.Type != *filter_.CartType { + return false + } + } + + // Filter by status - if no status is specified, only show active carts + if filter_.GetStatus() != "" { + if string(c.Status) != filter_.GetStatus() { + return false + } + } else if c.Status == types.StatusDeleted { + return false + } + + // Filter by time range + if filter_.TimeRangeFilter != nil { + if filter_.StartTime != nil && c.CreatedAt.Before(*filter_.StartTime) { + return false + } + if filter_.EndTime != nil && c.CreatedAt.After(*filter_.EndTime) { + return false + } + } + + return true +} + +// cartSortFn implements sorting logic for carts +func cartSortFn(i, j *cart.Cart) bool { + if i == nil || j == nil { + return false + } + return i.CreatedAt.After(j.CreatedAt) +} + +func (s *InMemoryCartStore) Create(ctx context.Context, c *cart.Cart) error { + if c == nil { + return ierr.NewError("cart cannot be nil"). + WithHint("Cart data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if c.CreatedAt.IsZero() { + c.CreatedAt = now + } + if c.UpdatedAt.IsZero() { + c.UpdatedAt = now + } + + err := s.InMemoryStore.Create(ctx, c.ID, c) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("A cart with this ID already exists"). + WithReportableDetails(map[string]any{ + "cart_id": c.ID, + "user_id": c.UserID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create cart"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryCartStore) CreateWithLineItems(ctx context.Context, c *cart.Cart) error { + if c == nil { + return ierr.NewError("cart cannot be nil"). + WithHint("Cart data is required"). + Mark(ierr.ErrValidation) + } + + // Create the cart first + if err := s.Create(ctx, c); err != nil { + return err + } + + // Create line items if they exist + if len(c.LineItems) > 0 { + for _, item := range c.LineItems { + item.CartID = c.ID + if err := s.CreateCartLineItem(ctx, item); err != nil { + return err + } + } + } + + return nil +} + +func (s *InMemoryCartStore) Get(ctx context.Context, id string) (*cart.Cart, error) { + c, err := s.InMemoryStore.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("Cart with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "cart_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get cart with ID %s", id). + Mark(ierr.ErrDatabase) + } + + // Load line items + lineItems, err := s.ListCartLineItems(ctx, id) + if err != nil { + return nil, err + } + c.LineItems = lineItems + + return c, nil +} + +func (s *InMemoryCartStore) Update(ctx context.Context, c *cart.Cart) error { + if c == nil { + return ierr.NewError("cart cannot be nil"). + WithHint("Cart data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + c.UpdatedAt = time.Now().UTC() + + err := s.InMemoryStore.Update(ctx, c.ID, c) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Cart with ID %s was not found", c.ID). + WithReportableDetails(map[string]any{ + "cart_id": c.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update cart with ID %s", c.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryCartStore) Delete(ctx context.Context, id string) error { + // Get the cart first + c, err := s.Get(ctx, id) + if err != nil { + return err + } + + // Soft delete by setting status to deleted + c.Status = types.StatusDeleted + c.UpdatedAt = time.Now().UTC() + + return s.Update(ctx, c) +} + +func (s *InMemoryCartStore) Count(ctx context.Context, filter *types.CartFilter) (int, error) { + count, err := s.InMemoryStore.Count(ctx, filter, cartFilterFn) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count carts"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return count, nil +} + +func (s *InMemoryCartStore) List(ctx context.Context, filter *types.CartFilter) ([]*cart.Cart, error) { + carts, err := s.InMemoryStore.List(ctx, filter, cartFilterFn, cartSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list carts"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return carts, nil +} + +func (s *InMemoryCartStore) ListAll(ctx context.Context, filter *types.CartFilter) ([]*cart.Cart, error) { + if filter == nil { + filter = types.NewNoLimitCartFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + if !filter.IsUnlimited() { + filter.QueryFilter.Limit = nil + } + + if err := filter.Validate(); err != nil { + return nil, ierr.WithError(err). + WithHint("Invalid filter parameters"). + Mark(ierr.ErrValidation) + } + + // Create an unlimited filter + unlimitedFilter := &types.CartFilter{ + QueryFilter: types.NewNoLimitQueryFilter(), + TimeRangeFilter: filter.TimeRangeFilter, + UserID: filter.UserID, + EntityID: filter.EntityID, + EntityType: filter.EntityType, + CartType: filter.CartType, + ExpiresAt: filter.ExpiresAt, + } + + return s.List(ctx, unlimitedFilter) +} + +// Cart Line Items methods + +func (s *InMemoryCartStore) CreateCartLineItem(ctx context.Context, item *cart.CartLineItem) error { + if item == nil { + return ierr.NewError("cart line item cannot be nil"). + WithHint("Cart line item data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if item.CreatedAt.IsZero() { + item.CreatedAt = now + } + if item.UpdatedAt.IsZero() { + item.UpdatedAt = now + } + + err := s.lineItems.Create(ctx, item.ID, item) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("A cart line item with this ID already exists"). + WithReportableDetails(map[string]any{ + "line_item_id": item.ID, + "cart_id": item.CartID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create cart line item"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryCartStore) GetCartLineItem(ctx context.Context, id string) (*cart.CartLineItem, error) { + item, err := s.lineItems.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("Cart line item with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "line_item_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get cart line item with ID %s", id). + Mark(ierr.ErrDatabase) + } + return item, nil +} + +func (s *InMemoryCartStore) UpdateCartLineItem(ctx context.Context, item *cart.CartLineItem) error { + if item == nil { + return ierr.NewError("cart line item cannot be nil"). + WithHint("Cart line item data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + item.UpdatedAt = time.Now().UTC() + + err := s.lineItems.Update(ctx, item.ID, item) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Cart line item with ID %s was not found", item.ID). + WithReportableDetails(map[string]any{ + "line_item_id": item.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update cart line item with ID %s", item.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryCartStore) DeleteCartLineItem(ctx context.Context, id string) error { + err := s.lineItems.Delete(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Cart line item with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "line_item_id": id, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to delete cart line item with ID %s", id). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryCartStore) ListCartLineItems(ctx context.Context, cartId string) ([]*cart.CartLineItem, error) { + allItems, err := s.lineItems.List(ctx, nil, nil, nil) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list cart line items"). + Mark(ierr.ErrDatabase) + } + + // Filter by cart ID + items := lo.Filter(allItems, func(item *cart.CartLineItem, _ int) bool { + return item.CartID == cartId && item.Status != types.StatusDeleted + }) + + return items, nil +} + +// Clear clears both cart and line item stores +func (s *InMemoryCartStore) Clear() { + s.InMemoryStore.Clear() + s.lineItems.Clear() +} diff --git a/internal/testutil/inmemory_discount_store.go b/internal/testutil/inmemory_discount_store.go new file mode 100644 index 0000000..3ff77d0 --- /dev/null +++ b/internal/testutil/inmemory_discount_store.go @@ -0,0 +1,273 @@ +package testutil + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/domain/discount" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// InMemoryDiscountStore implements discount.Repository +type InMemoryDiscountStore struct { + *InMemoryStore[*discount.Discount] +} + +// NewInMemoryDiscountStore creates a new in-memory discount store +func NewInMemoryDiscountStore() *InMemoryDiscountStore { + return &InMemoryDiscountStore{ + InMemoryStore: NewInMemoryStore[*discount.Discount](), + } +} + +// discountFilterFn implements filtering logic for discounts +func discountFilterFn(ctx context.Context, d *discount.Discount, filter interface{}) bool { + if d == nil { + return false + } + + filter_, ok := filter.(*types.DiscountFilter) + if !ok { + return true // No filter applied + } + + // Filter by codes + if len(filter_.Codes) > 0 { + if !lo.Contains(filter_.Codes, d.Code) { + return false + } + } + + // Filter by discount IDs + if len(filter_.DiscountIDs) > 0 { + if !lo.Contains(filter_.DiscountIDs, d.ID) { + return false + } + } + + // Filter by discount type + if filter_.DiscountType != "" { + if d.DiscountType != filter_.DiscountType { + return false + } + } + + // Filter by is combinable + if filter_.IsCombinable { + if !d.IsCombinable { + return false + } + } + + // Filter by status - if no status is specified, only show active discounts + if filter_.GetStatus() != "" { + if string(d.Status) != filter_.GetStatus() { + return false + } + } else if d.Status == types.StatusDeleted { + return false + } + + // Filter by time range + if filter_.TimeRangeFilter != nil { + if filter_.StartTime != nil && d.CreatedAt.Before(*filter_.StartTime) { + return false + } + if filter_.EndTime != nil && d.CreatedAt.After(*filter_.EndTime) { + return false + } + } + + return true +} + +// discountSortFn implements sorting logic for discounts +func discountSortFn(i, j *discount.Discount) bool { + if i == nil || j == nil { + return false + } + return i.CreatedAt.After(j.CreatedAt) +} + +func (s *InMemoryDiscountStore) Create(ctx context.Context, d *discount.Discount) error { + if d == nil { + return ierr.NewError("discount cannot be nil"). + WithHint("Discount data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if d.CreatedAt.IsZero() { + d.CreatedAt = now + } + if d.UpdatedAt.IsZero() { + d.UpdatedAt = now + } + + err := s.InMemoryStore.Create(ctx, d.ID, d) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("A discount with this ID already exists"). + WithReportableDetails(map[string]any{ + "discount_id": d.ID, + "code": d.Code, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create discount"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryDiscountStore) Get(ctx context.Context, id string) (*discount.Discount, error) { + discount, err := s.InMemoryStore.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("Discount with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "discount_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get discount with ID %s", id). + Mark(ierr.ErrDatabase) + } + return discount, nil +} + +func (s *InMemoryDiscountStore) GetByCode(ctx context.Context, code string) (*discount.Discount, error) { + discounts, err := s.InMemoryStore.List(ctx, nil, discountFilterFn, discountSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to get discount by code"). + Mark(ierr.ErrDatabase) + } + + for _, d := range discounts { + if d.Code == code && d.Status != types.StatusDeleted { + return d, nil + } + } + + return nil, ierr.NewError("discount not found"). + WithHintf("Discount with code %s was not found", code). + WithReportableDetails(map[string]any{ + "code": code, + }). + Mark(ierr.ErrNotFound) +} + +func (s *InMemoryDiscountStore) Update(ctx context.Context, d *discount.Discount) error { + if d == nil { + return ierr.NewError("discount cannot be nil"). + WithHint("Discount data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + d.UpdatedAt = time.Now().UTC() + + err := s.InMemoryStore.Update(ctx, d.ID, d) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Discount with ID %s was not found", d.ID). + WithReportableDetails(map[string]any{ + "discount_id": d.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update discount with ID %s", d.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryDiscountStore) Delete(ctx context.Context, id string) error { + // Get the discount first + d, err := s.Get(ctx, id) + if err != nil { + return err + } + + // Soft delete by setting status to deleted + d.Status = types.StatusDeleted + d.UpdatedAt = time.Now().UTC() + + return s.Update(ctx, d) +} + +func (s *InMemoryDiscountStore) Count(ctx context.Context, filter *types.DiscountFilter) (int, error) { + count, err := s.InMemoryStore.Count(ctx, filter, discountFilterFn) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count discounts"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return count, nil +} + +func (s *InMemoryDiscountStore) List(ctx context.Context, filter *types.DiscountFilter) ([]*discount.Discount, error) { + discounts, err := s.InMemoryStore.List(ctx, filter, discountFilterFn, discountSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list discounts"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return discounts, nil +} + +func (s *InMemoryDiscountStore) ListAll(ctx context.Context, filter *types.DiscountFilter) ([]*discount.Discount, error) { + if filter == nil { + filter = types.NewNoLimitDiscountFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + if !filter.IsUnlimited() { + filter.QueryFilter.Limit = nil + } + + if err := filter.Validate(); err != nil { + return nil, ierr.WithError(err). + WithHint("Invalid filter parameters"). + Mark(ierr.ErrValidation) + } + + // Create an unlimited filter + unlimitedFilter := &types.DiscountFilter{ + QueryFilter: types.NewNoLimitQueryFilter(), + TimeRangeFilter: filter.TimeRangeFilter, + DiscountType: filter.DiscountType, + ValidFrom: filter.ValidFrom, + ValidUntil: filter.ValidUntil, + MinOrderValue: filter.MinOrderValue, + IsCombinable: filter.IsCombinable, + Codes: filter.Codes, + DiscountIDs: filter.DiscountIDs, + } + + return s.List(ctx, unlimitedFilter) +} + +// Clear clears the discount store +func (s *InMemoryDiscountStore) Clear() { + s.InMemoryStore.Clear() +} diff --git a/internal/testutil/inmemory_fileupload_store.go b/internal/testutil/inmemory_fileupload_store.go new file mode 100644 index 0000000..b284dd8 --- /dev/null +++ b/internal/testutil/inmemory_fileupload_store.go @@ -0,0 +1,104 @@ +package testutil + +import ( + "context" + "time" + + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/fileupload" + "github.com/omkar273/codegeeky/internal/types" +) + +// InMemoryFileUploadStore implements fileupload.Repository +type InMemoryFileUploadStore struct { + *InMemoryStore[*fileupload.FileUpload] +} + +// NewInMemoryFileUploadStore creates a new in-memory file upload store +func NewInMemoryFileUploadStore() *InMemoryFileUploadStore { + return &InMemoryFileUploadStore{ + InMemoryStore: NewInMemoryStore[*fileupload.FileUpload](), + } +} + +func (s *InMemoryFileUploadStore) Create(ctx context.Context, f *fileupload.FileUpload) (*fileupload.FileUpload, error) { + if f == nil { + return nil, ierr.NewError("file upload cannot be nil"). + WithHint("File upload data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if f.CreatedAt.IsZero() { + f.CreatedAt = now + } + if f.UpdatedAt.IsZero() { + f.UpdatedAt = now + } + + err := s.InMemoryStore.Create(ctx, f.ID, f) + if err != nil { + if err.Error() == "item already exists" { + return nil, ierr.WithError(err). + WithHint("A file upload with this ID already exists"). + WithReportableDetails(map[string]any{ + "file_id": f.ID, + "external_id": f.ExternalID, + }). + Mark(ierr.ErrAlreadyExists) + } + return nil, ierr.WithError(err). + WithHint("Failed to create file upload"). + Mark(ierr.ErrDatabase) + } + return f, nil +} + +func (s *InMemoryFileUploadStore) Get(ctx context.Context, id string) (*fileupload.FileUpload, error) { + file, err := s.InMemoryStore.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("File upload with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "file_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get file upload with ID %s", id). + Mark(ierr.ErrDatabase) + } + return file, nil +} + +func (s *InMemoryFileUploadStore) Delete(ctx context.Context, id string) error { + // Get the file first + f, err := s.Get(ctx, id) + if err != nil { + return err + } + + // Soft delete by setting status to deleted + f.Status = types.StatusDeleted + f.UpdatedAt = time.Now().UTC() + + return s.InMemoryStore.Update(ctx, f.ID, f) +} + +func (s *InMemoryFileUploadStore) Exists(ctx context.Context, id string) (bool, error) { + _, err := s.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return false, nil + } + return false, err + } + return true, nil +} + +// Clear clears the file upload store +func (s *InMemoryFileUploadStore) Clear() { + s.InMemoryStore.Clear() +} diff --git a/internal/testutil/inmemory_internship_batch_store.go b/internal/testutil/inmemory_internship_batch_store.go new file mode 100644 index 0000000..5ad304b --- /dev/null +++ b/internal/testutil/inmemory_internship_batch_store.go @@ -0,0 +1,257 @@ +package testutil + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/domain/internship" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// InMemoryInternshipBatchStore implements internship.InternshipBatchRepository +type InMemoryInternshipBatchStore struct { + *InMemoryStore[*internship.InternshipBatch] +} + +// NewInMemoryInternshipBatchStore creates a new in-memory internship batch store +func NewInMemoryInternshipBatchStore() *InMemoryInternshipBatchStore { + return &InMemoryInternshipBatchStore{ + InMemoryStore: NewInMemoryStore[*internship.InternshipBatch](), + } +} + +// internshipBatchFilterFn implements filtering logic for internship batches +func internshipBatchFilterFn(ctx context.Context, b *internship.InternshipBatch, filter interface{}) bool { + if b == nil { + return false + } + + filter_, ok := filter.(*types.InternshipBatchFilter) + if !ok { + return true // No filter applied + } + + // Filter by internship IDs + if len(filter_.InternshipIDs) > 0 { + if !lo.Contains(filter_.InternshipIDs, b.InternshipID) { + return false + } + } + + // Filter by name + if filter_.Name != "" { + if b.Name != filter_.Name { + return false + } + } + + // Filter by batch status + if filter_.BatchStatus != "" { + if b.BatchStatus != filter_.BatchStatus { + return false + } + } + + // Filter by start date + if filter_.StartDate != nil { + if b.StartDate.Before(*filter_.StartDate) { + return false + } + } + + // Filter by end date + if filter_.EndDate != nil { + if b.EndDate.After(*filter_.EndDate) { + return false + } + } + + // Filter by status - if no status is specified, only show active batches + if filter_.GetStatus() != "" { + if string(b.Status) != filter_.GetStatus() { + return false + } + } else if b.Status == types.StatusDeleted { + return false + } + + // Filter by time range + if filter_.TimeRangeFilter != nil { + if filter_.StartTime != nil && b.CreatedAt.Before(*filter_.StartTime) { + return false + } + if filter_.EndTime != nil && b.CreatedAt.After(*filter_.EndTime) { + return false + } + } + + return true +} + +// internshipBatchSortFn implements sorting logic for internship batches +func internshipBatchSortFn(i, j *internship.InternshipBatch) bool { + if i == nil || j == nil { + return false + } + return i.CreatedAt.After(j.CreatedAt) +} + +func (s *InMemoryInternshipBatchStore) Create(ctx context.Context, b *internship.InternshipBatch) error { + if b == nil { + return ierr.NewError("internship batch cannot be nil"). + WithHint("Internship batch data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if b.CreatedAt.IsZero() { + b.CreatedAt = now + } + if b.UpdatedAt.IsZero() { + b.UpdatedAt = now + } + + err := s.InMemoryStore.Create(ctx, b.ID, b) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("An internship batch with this ID already exists"). + WithReportableDetails(map[string]any{ + "batch_id": b.ID, + "internship_id": b.InternshipID, + "name": b.Name, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create internship batch"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryInternshipBatchStore) Get(ctx context.Context, id string) (*internship.InternshipBatch, error) { + batch, err := s.InMemoryStore.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("Internship batch with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "batch_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get internship batch with ID %s", id). + Mark(ierr.ErrDatabase) + } + return batch, nil +} + +func (s *InMemoryInternshipBatchStore) Update(ctx context.Context, b *internship.InternshipBatch) error { + if b == nil { + return ierr.NewError("internship batch cannot be nil"). + WithHint("Internship batch data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + b.UpdatedAt = time.Now().UTC() + + err := s.InMemoryStore.Update(ctx, b.ID, b) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Internship batch with ID %s was not found", b.ID). + WithReportableDetails(map[string]any{ + "batch_id": b.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update internship batch with ID %s", b.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryInternshipBatchStore) Delete(ctx context.Context, id string) error { + // Get the batch first + b, err := s.Get(ctx, id) + if err != nil { + return err + } + + // Soft delete by setting status to deleted + b.Status = types.StatusDeleted + b.UpdatedAt = time.Now().UTC() + + return s.Update(ctx, b) +} + +func (s *InMemoryInternshipBatchStore) Count(ctx context.Context, filter *types.InternshipBatchFilter) (int, error) { + count, err := s.InMemoryStore.Count(ctx, filter, internshipBatchFilterFn) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count internship batches"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return count, nil +} + +func (s *InMemoryInternshipBatchStore) List(ctx context.Context, filter *types.InternshipBatchFilter) ([]*internship.InternshipBatch, error) { + batches, err := s.InMemoryStore.List(ctx, filter, internshipBatchFilterFn, internshipBatchSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list internship batches"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return batches, nil +} + +func (s *InMemoryInternshipBatchStore) ListAll(ctx context.Context, filter *types.InternshipBatchFilter) ([]*internship.InternshipBatch, error) { + if filter == nil { + filter = types.NewNoLimitInternshipBatchFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + if !filter.IsUnlimited() { + filter.QueryFilter.Limit = nil + } + + if err := filter.Validate(); err != nil { + return nil, ierr.WithError(err). + WithHint("Invalid filter parameters"). + Mark(ierr.ErrValidation) + } + + // Create an unlimited filter + unlimitedFilter := &types.InternshipBatchFilter{ + QueryFilter: types.NewNoLimitQueryFilter(), + TimeRangeFilter: filter.TimeRangeFilter, + InternshipIDs: filter.InternshipIDs, + Name: filter.Name, + BatchStatus: filter.BatchStatus, + StartDate: filter.StartDate, + EndDate: filter.EndDate, + } + + return s.List(ctx, unlimitedFilter) +} + +// Clear clears the internship batch store +func (s *InMemoryInternshipBatchStore) Clear() { + s.InMemoryStore.Clear() +} diff --git a/internal/testutil/inmemory_internship_enrollment_store.go b/internal/testutil/inmemory_internship_enrollment_store.go new file mode 100644 index 0000000..153ee23 --- /dev/null +++ b/internal/testutil/inmemory_internship_enrollment_store.go @@ -0,0 +1,288 @@ +package testutil + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/domain/internshipenrollment" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// InMemoryInternshipEnrollmentStore implements internshipenrollment.Repository +type InMemoryInternshipEnrollmentStore struct { + *InMemoryStore[*internshipenrollment.InternshipEnrollment] +} + +// NewInMemoryInternshipEnrollmentStore creates a new in-memory internship enrollment store +func NewInMemoryInternshipEnrollmentStore() *InMemoryInternshipEnrollmentStore { + return &InMemoryInternshipEnrollmentStore{ + InMemoryStore: NewInMemoryStore[*internshipenrollment.InternshipEnrollment](), + } +} + +// internshipEnrollmentFilterFn implements filtering logic for internship enrollments +func internshipEnrollmentFilterFn(ctx context.Context, e *internshipenrollment.InternshipEnrollment, filter interface{}) bool { + if e == nil { + return false + } + + filter_, ok := filter.(*types.InternshipEnrollmentFilter) + if !ok { + return true // No filter applied + } + + // Filter by user ID + if filter_.UserID != "" { + if e.UserID != filter_.UserID { + return false + } + } + + // Filter by internship IDs + if len(filter_.InternshipIDs) > 0 { + if !lo.Contains(filter_.InternshipIDs, e.InternshipID) { + return false + } + } + + // Filter by enrollment IDs + if len(filter_.EnrollmentIDs) > 0 { + if !lo.Contains(filter_.EnrollmentIDs, e.ID) { + return false + } + } + + // Filter by enrollment status + if filter_.EnrollmentStatus != "" { + if e.EnrollmentStatus != filter_.EnrollmentStatus { + return false + } + } + + // Filter by payment status + if filter_.PaymentStatus != "" { + if e.PaymentStatus != filter_.PaymentStatus { + return false + } + } + + // Filter by payment ID + if filter_.PaymentID != nil { + if e.PaymentID == nil || *e.PaymentID != *filter_.PaymentID { + return false + } + } + + // Filter by status - if no status is specified, only show active enrollments + if filter_.GetStatus() != "" { + if string(e.Status) != filter_.GetStatus() { + return false + } + } else if e.Status == types.StatusDeleted { + return false + } + + // Filter by time range + if filter_.TimeRangeFilter != nil { + if filter_.StartTime != nil && e.CreatedAt.Before(*filter_.StartTime) { + return false + } + if filter_.EndTime != nil && e.CreatedAt.After(*filter_.EndTime) { + return false + } + } + + return true +} + +// internshipEnrollmentSortFn implements sorting logic for internship enrollments +func internshipEnrollmentSortFn(i, j *internshipenrollment.InternshipEnrollment) bool { + if i == nil || j == nil { + return false + } + return i.CreatedAt.After(j.CreatedAt) +} + +func (s *InMemoryInternshipEnrollmentStore) Create(ctx context.Context, e *internshipenrollment.InternshipEnrollment) error { + if e == nil { + return ierr.NewError("internship enrollment cannot be nil"). + WithHint("Internship enrollment data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if e.CreatedAt.IsZero() { + e.CreatedAt = now + } + if e.UpdatedAt.IsZero() { + e.UpdatedAt = now + } + + err := s.InMemoryStore.Create(ctx, e.ID, e) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("An internship enrollment with this ID already exists"). + WithReportableDetails(map[string]any{ + "enrollment_id": e.ID, + "user_id": e.UserID, + "internship_id": e.InternshipID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create internship enrollment"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryInternshipEnrollmentStore) Get(ctx context.Context, id string) (*internshipenrollment.InternshipEnrollment, error) { + enrollment, err := s.InMemoryStore.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("Internship enrollment with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "enrollment_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get internship enrollment with ID %s", id). + Mark(ierr.ErrDatabase) + } + return enrollment, nil +} + +func (s *InMemoryInternshipEnrollmentStore) Update(ctx context.Context, e *internshipenrollment.InternshipEnrollment) error { + if e == nil { + return ierr.NewError("internship enrollment cannot be nil"). + WithHint("Internship enrollment data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + e.UpdatedAt = time.Now().UTC() + + err := s.InMemoryStore.Update(ctx, e.ID, e) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Internship enrollment with ID %s was not found", e.ID). + WithReportableDetails(map[string]any{ + "enrollment_id": e.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update internship enrollment with ID %s", e.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryInternshipEnrollmentStore) Delete(ctx context.Context, id string) error { + // Get the enrollment first + e, err := s.Get(ctx, id) + if err != nil { + return err + } + + // Soft delete by setting status to deleted + e.Status = types.StatusDeleted + e.UpdatedAt = time.Now().UTC() + + return s.Update(ctx, e) +} + +func (s *InMemoryInternshipEnrollmentStore) Count(ctx context.Context, filter *types.InternshipEnrollmentFilter) (int, error) { + count, err := s.InMemoryStore.Count(ctx, filter, internshipEnrollmentFilterFn) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count internship enrollments"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return count, nil +} + +func (s *InMemoryInternshipEnrollmentStore) List(ctx context.Context, filter *types.InternshipEnrollmentFilter) ([]*internshipenrollment.InternshipEnrollment, error) { + enrollments, err := s.InMemoryStore.List(ctx, filter, internshipEnrollmentFilterFn, internshipEnrollmentSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list internship enrollments"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return enrollments, nil +} + +func (s *InMemoryInternshipEnrollmentStore) ListAll(ctx context.Context, filter *types.InternshipEnrollmentFilter) ([]*internshipenrollment.InternshipEnrollment, error) { + if filter == nil { + filter = types.NewNoLimitInternshipEnrollmentFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + if !filter.IsUnlimited() { + filter.QueryFilter.Limit = nil + } + + if err := filter.Validate(); err != nil { + return nil, ierr.WithError(err). + WithHint("Invalid filter parameters"). + Mark(ierr.ErrValidation) + } + + // Create an unlimited filter + unlimitedFilter := &types.InternshipEnrollmentFilter{ + QueryFilter: types.NewNoLimitQueryFilter(), + TimeRangeFilter: filter.TimeRangeFilter, + InternshipIDs: filter.InternshipIDs, + UserID: filter.UserID, + EnrollmentStatus: filter.EnrollmentStatus, + PaymentStatus: filter.PaymentStatus, + EnrollmentIDs: filter.EnrollmentIDs, + PaymentID: filter.PaymentID, + InternshipBatchID: filter.InternshipBatchID, + } + + return s.List(ctx, unlimitedFilter) +} + +func (s *InMemoryInternshipEnrollmentStore) GetByIdempotencyKey(ctx context.Context, idempotencyKey string) (*internshipenrollment.InternshipEnrollment, error) { + enrollments, err := s.InMemoryStore.List(ctx, nil, internshipEnrollmentFilterFn, internshipEnrollmentSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to get internship enrollment by idempotency key"). + Mark(ierr.ErrDatabase) + } + + for _, e := range enrollments { + if e.IdempotencyKey != nil && *e.IdempotencyKey == idempotencyKey && e.Status != types.StatusDeleted { + return e, nil + } + } + + return nil, ierr.NewError("internship enrollment not found"). + WithHintf("Internship enrollment with idempotency key %s was not found", idempotencyKey). + WithReportableDetails(map[string]any{ + "idempotency_key": idempotencyKey, + }). + Mark(ierr.ErrNotFound) +} + +// Clear clears the internship enrollment store +func (s *InMemoryInternshipEnrollmentStore) Clear() { + s.InMemoryStore.Clear() +} diff --git a/internal/testutil/inmemory_internship_store.go b/internal/testutil/inmemory_internship_store.go new file mode 100644 index 0000000..71a308a --- /dev/null +++ b/internal/testutil/inmemory_internship_store.go @@ -0,0 +1,304 @@ +package testutil + +import ( + "context" + "strings" + "time" + + "github.com/omkar273/codegeeky/internal/domain/internship" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// InMemoryInternshipStore implements internship.InternshipRepository +type InMemoryInternshipStore struct { + *InMemoryStore[*internship.Internship] +} + +// NewInMemoryInternshipStore creates a new in-memory internship store +func NewInMemoryInternshipStore() *InMemoryInternshipStore { + return &InMemoryInternshipStore{ + InMemoryStore: NewInMemoryStore[*internship.Internship](), + } +} + +// internshipFilterFn implements filtering logic for internships +func internshipFilterFn(ctx context.Context, i *internship.Internship, filter interface{}) bool { + if i == nil { + return false + } + + filter_, ok := filter.(*types.InternshipFilter) + if !ok { + return true // No filter applied + } + + // Filter by name contains + if filter_.Name != "" { + if !strings.Contains(strings.ToLower(i.Title), strings.ToLower(filter_.Name)) { + return false + } + } + + // Filter by internship IDs + if len(filter_.InternshipIDs) > 0 { + if !lo.Contains(filter_.InternshipIDs, i.ID) { + return false + } + } + + // Filter by levels + if len(filter_.Levels) > 0 { + if !lo.Contains(filter_.Levels, i.Level) { + return false + } + } + + // Filter by modes + if len(filter_.Modes) > 0 { + if !lo.Contains(filter_.Modes, i.Mode) { + return false + } + } + + // Filter by category IDs + if len(filter_.CategoryIDs) > 0 { + hasCategory := false + for _, category := range i.Categories { + if lo.Contains(filter_.CategoryIDs, category.ID) { + hasCategory = true + break + } + } + if !hasCategory { + return false + } + } + + // Filter by duration in weeks + if filter_.DurationInWeeks > 0 { + if i.DurationInWeeks != filter_.DurationInWeeks { + return false + } + } + + // Filter by price range + if filter_.MinPrice != nil && i.Price.LessThan(*filter_.MinPrice) { + return false + } + if filter_.MaxPrice != nil && i.Price.GreaterThan(*filter_.MaxPrice) { + return false + } + + // Filter by status - if no status is specified, only show active internships + if filter_.GetStatus() != "" { + if string(i.Status) != filter_.GetStatus() { + return false + } + } else if i.Status == types.StatusDeleted { + return false + } + + // Filter by time range + if filter_.TimeRangeFilter != nil { + if filter_.StartTime != nil && i.CreatedAt.Before(*filter_.StartTime) { + return false + } + if filter_.EndTime != nil && i.CreatedAt.After(*filter_.EndTime) { + return false + } + } + + return true +} + +// internshipSortFn implements sorting logic for internships +func internshipSortFn(i, j *internship.Internship) bool { + if i == nil || j == nil { + return false + } + return i.CreatedAt.After(j.CreatedAt) +} + +func (s *InMemoryInternshipStore) Create(ctx context.Context, i *internship.Internship) error { + if i == nil { + return ierr.NewError("internship cannot be nil"). + WithHint("Internship data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if i.CreatedAt.IsZero() { + i.CreatedAt = now + } + if i.UpdatedAt.IsZero() { + i.UpdatedAt = now + } + + err := s.InMemoryStore.Create(ctx, i.ID, i) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("An internship with this ID already exists"). + WithReportableDetails(map[string]any{ + "internship_id": i.ID, + "title": i.Title, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create internship"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryInternshipStore) Get(ctx context.Context, id string) (*internship.Internship, error) { + internship, err := s.InMemoryStore.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("Internship with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "internship_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get internship with ID %s", id). + Mark(ierr.ErrDatabase) + } + return internship, nil +} + +func (s *InMemoryInternshipStore) GetByLookupKey(ctx context.Context, lookupKey string) (*internship.Internship, error) { + internships, err := s.InMemoryStore.List(ctx, nil, internshipFilterFn, internshipSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to get internship by lookup key"). + Mark(ierr.ErrDatabase) + } + + for _, i := range internships { + if i.LookupKey == lookupKey && i.Status != types.StatusDeleted { + return i, nil + } + } + + return nil, ierr.NewError("internship not found"). + WithHintf("Internship with lookup key %s was not found", lookupKey). + WithReportableDetails(map[string]any{ + "lookup_key": lookupKey, + }). + Mark(ierr.ErrNotFound) +} + +func (s *InMemoryInternshipStore) Update(ctx context.Context, i *internship.Internship) error { + if i == nil { + return ierr.NewError("internship cannot be nil"). + WithHint("Internship data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + i.UpdatedAt = time.Now().UTC() + + err := s.InMemoryStore.Update(ctx, i.ID, i) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Internship with ID %s was not found", i.ID). + WithReportableDetails(map[string]any{ + "internship_id": i.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update internship with ID %s", i.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryInternshipStore) Delete(ctx context.Context, id string) error { + // Get the internship first + i, err := s.Get(ctx, id) + if err != nil { + return err + } + + // Soft delete by setting status to deleted + i.Status = types.StatusDeleted + i.UpdatedAt = time.Now().UTC() + + return s.Update(ctx, i) +} + +func (s *InMemoryInternshipStore) Count(ctx context.Context, filter *types.InternshipFilter) (int, error) { + count, err := s.InMemoryStore.Count(ctx, filter, internshipFilterFn) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count internships"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return count, nil +} + +func (s *InMemoryInternshipStore) List(ctx context.Context, filter *types.InternshipFilter) ([]*internship.Internship, error) { + internships, err := s.InMemoryStore.List(ctx, filter, internshipFilterFn, internshipSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list internships"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return internships, nil +} + +func (s *InMemoryInternshipStore) ListAll(ctx context.Context, filter *types.InternshipFilter) ([]*internship.Internship, error) { + if filter == nil { + filter = types.NewNoLimitInternshipFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + if !filter.IsUnlimited() { + filter.QueryFilter.Limit = nil + } + + if err := filter.Validate(); err != nil { + return nil, ierr.WithError(err). + WithHint("Invalid filter parameters"). + Mark(ierr.ErrValidation) + } + + // Create an unlimited filter + unlimitedFilter := &types.InternshipFilter{ + QueryFilter: types.NewNoLimitQueryFilter(), + TimeRangeFilter: filter.TimeRangeFilter, + Name: filter.Name, + CategoryIDs: filter.CategoryIDs, + Levels: filter.Levels, + Modes: filter.Modes, + InternshipIDs: filter.InternshipIDs, + DurationInWeeks: filter.DurationInWeeks, + MaxPrice: filter.MaxPrice, + MinPrice: filter.MinPrice, + } + + return s.List(ctx, unlimitedFilter) +} + +// Clear clears the internship store +func (s *InMemoryInternshipStore) Clear() { + s.InMemoryStore.Clear() +} diff --git a/internal/testutil/inmemory_payment_store.go b/internal/testutil/inmemory_payment_store.go new file mode 100644 index 0000000..bc4f068 --- /dev/null +++ b/internal/testutil/inmemory_payment_store.go @@ -0,0 +1,393 @@ +package testutil + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/domain/payment" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// InMemoryPaymentStore implements payment.Repository +type InMemoryPaymentStore struct { + *InMemoryStore[*payment.Payment] + attempts *InMemoryStore[*payment.PaymentAttempt] +} + +// NewInMemoryPaymentStore creates a new in-memory payment store +func NewInMemoryPaymentStore() *InMemoryPaymentStore { + return &InMemoryPaymentStore{ + InMemoryStore: NewInMemoryStore[*payment.Payment](), + attempts: NewInMemoryStore[*payment.PaymentAttempt](), + } +} + +// paymentFilterFn implements filtering logic for payments +func paymentFilterFn(ctx context.Context, p *payment.Payment, filter interface{}) bool { + if p == nil { + return false + } + + filter_, ok := filter.(*types.PaymentFilter) + if !ok { + return true // No filter applied + } + + // Filter by destination type + if filter_.DestinationType != nil { + if string(p.DestinationType) != *filter_.DestinationType { + return false + } + } + + // Filter by destination ID + if filter_.DestinationID != nil { + if p.DestinationID != *filter_.DestinationID { + return false + } + } + + // Filter by payment method type + if filter_.PaymentMethodType != nil { + if p.PaymentMethodType == nil || string(*p.PaymentMethodType) != *filter_.PaymentMethodType { + return false + } + } + + // Filter by payment gateway + if filter_.PaymentGateway != nil { + if string(p.PaymentGatewayProvider) != *filter_.PaymentGateway { + return false + } + } + + // Filter by payment status + if filter_.PaymentStatus != nil { + if string(p.PaymentStatus) != *filter_.PaymentStatus { + return false + } + } + + // Filter by currency + if filter_.Currency != nil { + if string(p.Currency) != *filter_.Currency { + return false + } + } + + // Filter by payment IDs + if len(filter_.PaymentIDs) > 0 { + if !lo.Contains(filter_.PaymentIDs, p.ID) { + return false + } + } + + // Filter by status - if no status is specified, only show active payments + if filter_.GetStatus() != "" { + if string(p.Status) != filter_.GetStatus() { + return false + } + } else if string(p.Status) == string(types.StatusDeleted) { + return false + } + + // Filter by time range + if filter_.TimeRangeFilter != nil { + if filter_.StartTime != nil && p.CreatedAt.Before(*filter_.StartTime) { + return false + } + if filter_.EndTime != nil && p.CreatedAt.After(*filter_.EndTime) { + return false + } + } + + return true +} + +// paymentSortFn implements sorting logic for payments +func paymentSortFn(i, j *payment.Payment) bool { + if i == nil || j == nil { + return false + } + return i.CreatedAt.After(j.CreatedAt) +} + +func (s *InMemoryPaymentStore) Create(ctx context.Context, p *payment.Payment) error { + if p == nil { + return ierr.NewError("payment cannot be nil"). + WithHint("Payment data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if p.CreatedAt.IsZero() { + p.CreatedAt = now + } + if p.UpdatedAt.IsZero() { + p.UpdatedAt = now + } + + err := s.InMemoryStore.Create(ctx, p.ID, p) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("A payment with this ID already exists"). + WithReportableDetails(map[string]any{ + "payment_id": p.ID, + "amount": p.Amount, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create payment"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryPaymentStore) Get(ctx context.Context, id string) (*payment.Payment, error) { + payment, err := s.InMemoryStore.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("Payment with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "payment_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get payment with ID %s", id). + Mark(ierr.ErrDatabase) + } + + // Load attempts + attempts, err := s.ListAttempts(ctx, id) + if err != nil { + return nil, err + } + payment.Attempts = attempts + + return payment, nil +} + +func (s *InMemoryPaymentStore) Update(ctx context.Context, p *payment.Payment) error { + if p == nil { + return ierr.NewError("payment cannot be nil"). + WithHint("Payment data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + p.UpdatedAt = time.Now().UTC() + + err := s.InMemoryStore.Update(ctx, p.ID, p) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Payment with ID %s was not found", p.ID). + WithReportableDetails(map[string]any{ + "payment_id": p.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update payment with ID %s", p.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryPaymentStore) Delete(ctx context.Context, id string) error { + // Get the payment first + p, err := s.Get(ctx, id) + if err != nil { + return err + } + + // Soft delete by setting status to deleted + p.Status = string(types.StatusDeleted) + p.UpdatedAt = time.Now().UTC() + + return s.Update(ctx, p) +} + +func (s *InMemoryPaymentStore) List(ctx context.Context, filter *types.PaymentFilter) ([]*payment.Payment, error) { + payments, err := s.InMemoryStore.List(ctx, filter, paymentFilterFn, paymentSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list payments"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return payments, nil +} + +func (s *InMemoryPaymentStore) Count(ctx context.Context, filter *types.PaymentFilter) (int, error) { + count, err := s.InMemoryStore.Count(ctx, filter, paymentFilterFn) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count payments"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return count, nil +} + +func (s *InMemoryPaymentStore) GetByIdempotencyKey(ctx context.Context, key string) (*payment.Payment, error) { + payments, err := s.InMemoryStore.List(ctx, nil, paymentFilterFn, paymentSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to get payment by idempotency key"). + Mark(ierr.ErrDatabase) + } + + for _, p := range payments { + if p.IdempotencyKey == key && string(p.Status) != string(types.StatusDeleted) { + return p, nil + } + } + + return nil, ierr.NewError("payment not found"). + WithHintf("Payment with idempotency key %s was not found", key). + WithReportableDetails(map[string]any{ + "idempotency_key": key, + }). + Mark(ierr.ErrNotFound) +} + +// Payment Attempt methods + +func (s *InMemoryPaymentStore) CreateAttempt(ctx context.Context, attempt *payment.PaymentAttempt) error { + if attempt == nil { + return ierr.NewError("payment attempt cannot be nil"). + WithHint("Payment attempt data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if attempt.CreatedAt.IsZero() { + attempt.CreatedAt = now + } + if attempt.UpdatedAt.IsZero() { + attempt.UpdatedAt = now + } + + err := s.attempts.Create(ctx, attempt.ID, attempt) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("A payment attempt with this ID already exists"). + WithReportableDetails(map[string]any{ + "attempt_id": attempt.ID, + "payment_id": attempt.PaymentID, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create payment attempt"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryPaymentStore) GetAttempt(ctx context.Context, id string) (*payment.PaymentAttempt, error) { + attempt, err := s.attempts.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("Payment attempt with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "attempt_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get payment attempt with ID %s", id). + Mark(ierr.ErrDatabase) + } + return attempt, nil +} + +func (s *InMemoryPaymentStore) UpdateAttempt(ctx context.Context, attempt *payment.PaymentAttempt) error { + if attempt == nil { + return ierr.NewError("payment attempt cannot be nil"). + WithHint("Payment attempt data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + attempt.UpdatedAt = time.Now().UTC() + + err := s.attempts.Update(ctx, attempt.ID, attempt) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("Payment attempt with ID %s was not found", attempt.ID). + WithReportableDetails(map[string]any{ + "attempt_id": attempt.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update payment attempt with ID %s", attempt.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryPaymentStore) ListAttempts(ctx context.Context, paymentID string) ([]*payment.PaymentAttempt, error) { + allAttempts, err := s.attempts.List(ctx, nil, nil, nil) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list payment attempts"). + Mark(ierr.ErrDatabase) + } + + // Filter by payment ID + attempts := lo.Filter(allAttempts, func(attempt *payment.PaymentAttempt, _ int) bool { + return attempt.PaymentID == paymentID && attempt.Status != types.StatusDeleted + }) + + return attempts, nil +} + +func (s *InMemoryPaymentStore) GetLatestAttempt(ctx context.Context, paymentID string) (*payment.PaymentAttempt, error) { + attempts, err := s.ListAttempts(ctx, paymentID) + if err != nil { + return nil, err + } + + if len(attempts) == 0 { + return nil, ierr.NewError("no payment attempts found"). + WithHintf("No payment attempts found for payment ID %s", paymentID). + WithReportableDetails(map[string]any{ + "payment_id": paymentID, + }). + Mark(ierr.ErrNotFound) + } + + // Return the attempt with the highest attempt number + latestAttempt := attempts[0] + for _, attempt := range attempts { + if attempt.AttemptNumber > latestAttempt.AttemptNumber { + latestAttempt = attempt + } + } + + return latestAttempt, nil +} + +// Clear clears both payment and attempt stores +func (s *InMemoryPaymentStore) Clear() { + s.InMemoryStore.Clear() + s.attempts.Clear() +} diff --git a/internal/testutil/inmemory_user_store.go b/internal/testutil/inmemory_user_store.go new file mode 100644 index 0000000..06449d7 --- /dev/null +++ b/internal/testutil/inmemory_user_store.go @@ -0,0 +1,263 @@ +package testutil + +import ( + "context" + "time" + + "github.com/omkar273/codegeeky/internal/domain/user" + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" + "github.com/samber/lo" +) + +// InMemoryUserStore implements user.Repository +type InMemoryUserStore struct { + *InMemoryStore[*user.User] +} + +// NewInMemoryUserStore creates a new in-memory user store +func NewInMemoryUserStore() *InMemoryUserStore { + return &InMemoryUserStore{ + InMemoryStore: NewInMemoryStore[*user.User](), + } +} + +// userFilterFn implements filtering logic for users +func userFilterFn(ctx context.Context, u *user.User, filter interface{}) bool { + if u == nil { + return false + } + + filter_, ok := filter.(*types.UserFilter) + if !ok { + return true // No filter applied + } + + // Filter by email + if len(filter_.Email) > 0 { + if !lo.Contains(filter_.Email, u.Email) { + return false + } + } + + // Filter by phone + if len(filter_.Phone) > 0 { + if !lo.Contains(filter_.Phone, u.Phone) { + return false + } + } + + // Filter by roles + if len(filter_.Roles) > 0 { + if !lo.Contains(filter_.Roles, string(u.Role)) { + return false + } + } + + // Filter by status - if no status is specified, only show active users + if filter_.Status != "" { + if u.Status != filter_.Status { + return false + } + } else if u.Status == types.StatusDeleted { + return false + } + + // Filter by time range + if filter_.TimeRangeFilter != nil { + if filter_.StartTime != nil && u.CreatedAt.Before(*filter_.StartTime) { + return false + } + if filter_.EndTime != nil && u.CreatedAt.After(*filter_.EndTime) { + return false + } + } + + return true +} + +// userSortFn implements sorting logic for users +func userSortFn(i, j *user.User) bool { + if i == nil || j == nil { + return false + } + return i.CreatedAt.After(j.CreatedAt) +} + +func (s *InMemoryUserStore) Create(ctx context.Context, u *user.User) error { + if u == nil { + return ierr.NewError("user cannot be nil"). + WithHint("User data is required"). + Mark(ierr.ErrValidation) + } + + // Set timestamps + now := time.Now().UTC() + if u.CreatedAt.IsZero() { + u.CreatedAt = now + } + if u.UpdatedAt.IsZero() { + u.UpdatedAt = now + } + + err := s.InMemoryStore.Create(ctx, u.ID, u) + if err != nil { + if err.Error() == "item already exists" { + return ierr.WithError(err). + WithHint("A user with this ID already exists"). + WithReportableDetails(map[string]any{ + "user_id": u.ID, + "email": u.Email, + }). + Mark(ierr.ErrAlreadyExists) + } + return ierr.WithError(err). + WithHint("Failed to create user"). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryUserStore) Get(ctx context.Context, id string) (*user.User, error) { + user, err := s.InMemoryStore.Get(ctx, id) + if err != nil { + if err.Error() == "item not found" { + return nil, ierr.WithError(err). + WithHintf("User with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "user_id": id, + }). + Mark(ierr.ErrNotFound) + } + return nil, ierr.WithError(err). + WithHintf("Failed to get user with ID %s", id). + Mark(ierr.ErrDatabase) + } + return user, nil +} + +func (s *InMemoryUserStore) GetByEmail(ctx context.Context, email string) (*user.User, error) { + users, err := s.InMemoryStore.List(ctx, nil, userFilterFn, userSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to get user by email"). + Mark(ierr.ErrDatabase) + } + + for _, u := range users { + if u.Email == email && u.Status != types.StatusDeleted { + return u, nil + } + } + + return nil, ierr.NewError("user not found"). + WithHintf("User with email %s was not found", email). + WithReportableDetails(map[string]any{ + "email": email, + }). + Mark(ierr.ErrNotFound) +} + +func (s *InMemoryUserStore) List(ctx context.Context, filter *types.UserFilter) ([]*user.User, error) { + users, err := s.InMemoryStore.List(ctx, filter, userFilterFn, userSortFn) + if err != nil { + return nil, ierr.WithError(err). + WithHint("Failed to list users"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return users, nil +} + +func (s *InMemoryUserStore) ListAll(ctx context.Context, filter *types.UserFilter) ([]*user.User, error) { + if filter == nil { + filter = types.NewNoLimitUserFilter() + } + + if filter.QueryFilter == nil { + filter.QueryFilter = types.NewNoLimitQueryFilter() + } + + if !filter.IsUnlimited() { + filter.QueryFilter.Limit = nil + } + + if err := filter.Validate(); err != nil { + return nil, ierr.WithError(err). + WithHint("Invalid filter parameters"). + Mark(ierr.ErrValidation) + } + + // Create an unlimited filter + unlimitedFilter := &types.UserFilter{ + QueryFilter: types.NewNoLimitQueryFilter(), + TimeRangeFilter: filter.TimeRangeFilter, + Email: filter.Email, + Phone: filter.Phone, + Roles: filter.Roles, + Status: filter.Status, + } + + return s.List(ctx, unlimitedFilter) +} + +func (s *InMemoryUserStore) Count(ctx context.Context, filter *types.UserFilter) (int, error) { + count, err := s.InMemoryStore.Count(ctx, filter, userFilterFn) + if err != nil { + return 0, ierr.WithError(err). + WithHint("Failed to count users"). + WithReportableDetails(map[string]any{ + "filter": filter, + }). + Mark(ierr.ErrDatabase) + } + return count, nil +} + +func (s *InMemoryUserStore) Update(ctx context.Context, u *user.User) error { + if u == nil { + return ierr.NewError("user cannot be nil"). + WithHint("User data is required"). + Mark(ierr.ErrValidation) + } + + // Update timestamp + u.UpdatedAt = time.Now().UTC() + + err := s.InMemoryStore.Update(ctx, u.ID, u) + if err != nil { + if err.Error() == "item not found" { + return ierr.WithError(err). + WithHintf("User with ID %s was not found", u.ID). + WithReportableDetails(map[string]any{ + "user_id": u.ID, + }). + Mark(ierr.ErrNotFound) + } + return ierr.WithError(err). + WithHintf("Failed to update user with ID %s", u.ID). + Mark(ierr.ErrDatabase) + } + return nil +} + +func (s *InMemoryUserStore) Delete(ctx context.Context, u *user.User) error { + if u == nil { + return ierr.NewError("user cannot be nil"). + WithHint("User data is required"). + Mark(ierr.ErrValidation) + } + + // Soft delete by setting status to deleted + u.Status = types.StatusDeleted + u.UpdatedAt = time.Now().UTC() + + return s.Update(ctx, u) +} + +// Clear clears the user store +func (s *InMemoryUserStore) Clear() { + s.InMemoryStore.Clear() +} diff --git a/internal/testutil/mock_postgres_client.go b/internal/testutil/mock_postgres_client.go new file mode 100644 index 0000000..2f985e6 --- /dev/null +++ b/internal/testutil/mock_postgres_client.go @@ -0,0 +1,52 @@ +package testutil + +import ( + "context" + + "github.com/omkar273/codegeeky/ent" + "github.com/omkar273/codegeeky/internal/logger" + "github.com/omkar273/codegeeky/internal/postgres" + "github.com/omkar273/codegeeky/internal/types" +) + +var _ postgres.IClient = (*MockPostgresClient)(nil) // Ensure MockPostgresClient implements IClient + +// MockPostgresClient is a mock implementation of postgres client for testing +type MockPostgresClient struct { + entClient *ent.Client + logger *logger.Logger +} + +// NewMockPostgresClient creates a new mock postgres client +func NewMockPostgresClient(logger *logger.Logger) postgres.IClient { + return &MockPostgresClient{ + logger: logger, + } +} + +// WithTx executes the given function within a transaction +func (c *MockPostgresClient) WithTx(ctx context.Context, fn func(context.Context) error) error { + // If we're already in a transaction, reuse it + if tx := c.TxFromContext(ctx); tx != nil { + return fn(ctx) + } + + // For testing, we just execute the function without a real transaction + return fn(ctx) +} + +// TxFromContext returns the transaction from context if it exists +func (c *MockPostgresClient) TxFromContext(ctx context.Context) *ent.Tx { + if tx, ok := ctx.Value(types.CtxDBTransaction).(*ent.Tx); ok { + return tx + } + return nil +} + +// Querier returns the ent client +func (c *MockPostgresClient) Querier(ctx context.Context) *ent.Client { + if tx := c.TxFromContext(ctx); tx != nil { + return tx.Client() + } + return c.entClient +} diff --git a/internal/testutil/store.go b/internal/testutil/store.go new file mode 100644 index 0000000..229dfbf --- /dev/null +++ b/internal/testutil/store.go @@ -0,0 +1,217 @@ +package testutil + +import ( + "context" + "sort" + "sync" + + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/omkar273/codegeeky/internal/types" +) + +// FilterFunc is a generic filter function type +type FilterFunc[T any] func(ctx context.Context, item T, filter interface{}) bool + +// SortFunc is a generic sort function type +type SortFunc[T any] func(i, j T) bool + +// ExpandFunc is a function type for handling expand operations +type ExpandFunc[T any] func(ctx context.Context, item T, expand types.Expand) (T, error) + +// InMemoryStore implements a generic in-memory store with expand support +type InMemoryStore[T any] struct { + mu sync.RWMutex + items map[string]T + expandFn ExpandFunc[T] +} + +// NewInMemoryStore creates a new InMemoryStore +func NewInMemoryStore[T any]() *InMemoryStore[T] { + return &InMemoryStore[T]{ + items: make(map[string]T), + } +} + +// NewInMemoryStoreWithExpand creates a new InMemoryStore with expand functionality +func NewInMemoryStoreWithExpand[T any](expandFn ExpandFunc[T]) *InMemoryStore[T] { + return &InMemoryStore[T]{ + items: make(map[string]T), + expandFn: expandFn, + } +} + +// Create adds a new item to the store +func (s *InMemoryStore[T]) Create(ctx context.Context, id string, item T) error { + s.mu.Lock() + defer s.mu.Unlock() + + if _, exists := s.items[id]; exists { + return ierr.NewError("item already exists"). + WithHint("An item with this ID already exists"). + WithReportableDetails(map[string]any{ + "id": id, + }). + Mark(ierr.ErrAlreadyExists) + } + + s.items[id] = item + return nil +} + +// Get retrieves an item by ID with optional expand support +func (s *InMemoryStore[T]) Get(ctx context.Context, id string) (T, error) { + return s.GetWithExpand(ctx, id, types.NewExpand("")) +} + +// GetWithExpand retrieves an item by ID with expand functionality +func (s *InMemoryStore[T]) GetWithExpand(ctx context.Context, id string, expand types.Expand) (T, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + if item, exists := s.items[id]; exists { + if s.expandFn != nil && !expand.IsEmpty() { + return s.expandFn(ctx, item, expand) + } + return item, nil + } + + var zero T + return zero, ierr.NewError("item not found"). + WithHintf("Item with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "id": id, + }). + Mark(ierr.ErrNotFound) +} + +// List retrieves items based on filter with optional expand support +func (s *InMemoryStore[T]) List(ctx context.Context, filter interface{}, filterFn FilterFunc[T], sortFn SortFunc[T]) ([]T, error) { + return s.ListWithExpand(ctx, filter, filterFn, sortFn, types.NewExpand("")) +} + +// ListWithExpand retrieves items based on filter with expand functionality +func (s *InMemoryStore[T]) ListWithExpand(ctx context.Context, filter interface{}, filterFn FilterFunc[T], sortFn SortFunc[T], expand types.Expand) ([]T, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + var result []T + for _, item := range s.items { + if filterFn == nil || filterFn(ctx, item, filter) { + // Apply expand if available + if s.expandFn != nil && !expand.IsEmpty() { + expandedItem, err := s.expandFn(ctx, item, expand) + if err != nil { + return nil, err + } + result = append(result, expandedItem) + } else { + result = append(result, item) + } + } + } + + if sortFn != nil { + sort.Slice(result, func(i, j int) bool { + return sortFn(result[i], result[j]) + }) + } + + // Apply pagination if filter implements BaseFilter + if f, ok := filter.(types.BaseFilter); ok && !f.IsUnlimited() { + start := f.GetOffset() + if start >= len(result) { + return []T{}, nil + } + + end := start + f.GetLimit() + if end > len(result) { + end = len(result) + } + return result[start:end], nil + } + + return result, nil +} + +// Count returns the total number of items matching the filter +func (s *InMemoryStore[T]) Count(ctx context.Context, filter interface{}, filterFn FilterFunc[T]) (int, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + count := 0 + for _, item := range s.items { + if filterFn == nil || filterFn(ctx, item, filter) { + count++ + } + } + + return count, nil +} + +// Update updates an existing item +func (s *InMemoryStore[T]) Update(ctx context.Context, id string, item T) error { + s.mu.Lock() + defer s.mu.Unlock() + + if _, exists := s.items[id]; !exists { + return ierr.NewError("item not found"). + WithHintf("Item with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "id": id, + }). + Mark(ierr.ErrNotFound) + } + + s.items[id] = item + return nil +} + +// Delete removes an item from the store +func (s *InMemoryStore[T]) Delete(ctx context.Context, id string) error { + s.mu.Lock() + defer s.mu.Unlock() + + if _, exists := s.items[id]; !exists { + return ierr.NewError("item not found"). + WithHintf("Item with ID %s was not found", id). + WithReportableDetails(map[string]any{ + "id": id, + }). + Mark(ierr.ErrNotFound) + } + + delete(s.items, id) + return nil +} + +// Clear removes all items from the store +func (s *InMemoryStore[T]) Clear() { + s.mu.Lock() + defer s.mu.Unlock() + s.items = make(map[string]T) +} + +// GetAll returns all items in the store (for testing purposes) +func (s *InMemoryStore[T]) GetAll() map[string]T { + s.mu.RLock() + defer s.mu.RUnlock() + + // Create a copy to avoid race conditions + result := make(map[string]T) + for k, v := range s.items { + result[k] = v + } + return result +} + +// SetExpandFunction sets the expand function for the store +func (s *InMemoryStore[T]) SetExpandFunction(expandFn ExpandFunc[T]) { + s.mu.Lock() + defer s.mu.Unlock() + s.expandFn = expandFn +} + +// CheckEnvironmentFilter is a helper function to check if an item matches the environment filter +func CheckEnvironmentFilter(ctx context.Context, itemEnvID string) bool { + return true +} diff --git a/internal/types/cart.go b/internal/types/cart.go new file mode 100644 index 0000000..d7acb5d --- /dev/null +++ b/internal/types/cart.go @@ -0,0 +1,152 @@ +package types + +import ( + "fmt" + "slices" + "time" + + ierr "github.com/omkar273/codegeeky/internal/errors" +) + +type CartType string + +const ( + CartTypeOneTime CartType = "onetime" + CartTypeDefault CartType = "default" +) + +func (c CartType) String() string { + return string(c) +} + +func (c CartType) Validate() error { + allowedValues := []CartType{CartTypeOneTime, CartTypeDefault} + + if !slices.Contains(allowedValues, c) { + return ierr.NewError("INVALID_CART_TYPE"). + WithHint(fmt.Sprintf("allowed values are: %s", allowedValues)). + WithReportableDetails(map[string]any{ + "allowed_values": allowedValues, + }). + Mark(ierr.ErrValidation) + } + return nil +} + +type CartLineItemEntityType string + +const ( + CartLineItemEntityTypeInternship CartLineItemEntityType = "internship" + CartLineItemEntityTypeCourse CartLineItemEntityType = "course" +) + +func (c CartLineItemEntityType) String() string { + return string(c) +} + +func (c CartLineItemEntityType) Validate() error { + allowedValues := []CartLineItemEntityType{CartLineItemEntityTypeInternship, CartLineItemEntityTypeCourse} + + if !slices.Contains(allowedValues, c) { + return ierr.NewError("INVALID_CART_LINE_ITEM_ENTITY_TYPE"). + WithHint(fmt.Sprintf("allowed values are: %s", allowedValues)). + WithReportableDetails(map[string]any{ + "allowed_values": allowedValues, + }). + Mark(ierr.ErrValidation) + } + return nil +} + +type CartFilter struct { + *QueryFilter + *TimeRangeFilter + + // These fields are used to filter carts by user id + UserID string `json:"user_id,omitempty" form:"user_id" validate:"omitempty"` + EntityID string `json:"entity_id,omitempty" form:"entity_id" validate:"omitempty"` + EntityType CartLineItemEntityType `json:"entity_type,omitempty" form:"entity_type" validate:"omitempty"` + CartType *CartType `json:"cart_type,omitempty" form:"cart_type" validate:"omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty" form:"expires_at" validate:"omitempty"` +} + +func (f *CartFilter) Validate() error { + if err := f.QueryFilter.Validate(); err != nil { + return err + } + + if err := f.TimeRangeFilter.Validate(); err != nil { + return err + } + + return nil +} + +func NewCartFilter() *CartFilter { + return &CartFilter{ + QueryFilter: NewDefaultQueryFilter(), + TimeRangeFilter: &TimeRangeFilter{}, + } +} + +func NewNoLimitCartFilter() *CartFilter { + return &CartFilter{ + QueryFilter: NewNoLimitQueryFilter(), + TimeRangeFilter: &TimeRangeFilter{}, + } +} + +// GetLimit implements BaseFilter interface +func (f *CartFilter) GetLimit() int { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetLimit() + } + return f.QueryFilter.GetLimit() +} + +// GetOffset implements BaseFilter interface +func (f *CartFilter) GetOffset() int { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetOffset() + } + return f.QueryFilter.GetOffset() +} + +// GetStatus implements BaseFilter interface +func (f *CartFilter) GetStatus() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetStatus() + } + return f.QueryFilter.GetStatus() +} + +// GetSort implements BaseFilter interface +func (f *CartFilter) GetSort() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetSort() + } + return f.QueryFilter.GetSort() +} + +// GetOrder implements BaseFilter interface +func (f *CartFilter) GetOrder() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetOrder() + } + return f.QueryFilter.GetOrder() +} + +// GetExpand implements BaseFilter interface +func (f *CartFilter) GetExpand() Expand { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetExpand() + } + return f.QueryFilter.GetExpand() +} + +func (f *CartFilter) IsUnlimited() bool { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().IsUnlimited() + } + return f.QueryFilter.IsUnlimited() +} diff --git a/internal/types/context.go b/internal/types/context.go index 4150ed8..33e4886 100644 --- a/internal/types/context.go +++ b/internal/types/context.go @@ -17,7 +17,11 @@ const ( CtxIsGuest ContextKey = "ctx_is_guest" // Default values - DefaultUserID = "00000000-0000-0000-0000-000000000000" + DefaultUserID = "00000000-0000-0000-0000-000000000000" + DefaultRequestID = "00000000-0000-0000-0000-000000000000" + DefaultUserEmail = "test@test.com" + DefaultUserRole = UserRoleAdmin + DefaultIsGuest = true ) func GetUserID(ctx context.Context) string { diff --git a/internal/types/currency.go b/internal/types/currency.go index 31c2af2..a9fc0b7 100644 --- a/internal/types/currency.go +++ b/internal/types/currency.go @@ -23,5 +23,3 @@ func (c Currency) Validate() error { } return nil } - - diff --git a/internal/types/discount.go b/internal/types/discount.go index 060bf64..90a7b19 100644 --- a/internal/types/discount.go +++ b/internal/types/discount.go @@ -38,6 +38,8 @@ type DiscountFilter struct { ValidUntil *time.Time `json:"valid_until,omitempty" form:"valid_until" validate:"omitempty"` MinOrderValue *decimal.Decimal `json:"min_order_value,omitempty" form:"min_order_value" validate:"omitempty"` IsCombinable bool `json:"is_combinable,omitempty" form:"is_combinable" validate:"omitempty"` + Codes []string `json:"codes,omitempty" form:"codes" validate:"omitempty"` + DiscountIDs []string `json:"discount_ids,omitempty" form:"discount_ids" validate:"omitempty"` } func (f *DiscountFilter) Validate() error { diff --git a/internal/types/expand.go b/internal/types/expand.go index 32c0c3d..854a7ea 100644 --- a/internal/types/expand.go +++ b/internal/types/expand.go @@ -12,8 +12,8 @@ type ExpandableField string // Common expandable fields const ( - ExpandStation ExpandableField = "station" - ExpandCategory ExpandableField = "category" + ExpandCategory ExpandableField = "category" + ExpandCartLineItems ExpandableField = "cart_line_items" ) // ExpandConfig defines which fields can be expanded and their nested expansions @@ -27,12 +27,6 @@ type ExpandConfig struct { // Common expand configurations var ( // UserExpandConfig defines what can be expanded on a user - UserExpandConfig = ExpandConfig{ - AllowedFields: []ExpandableField{ExpandStation}, - NestedExpands: map[ExpandableField][]ExpandableField{ - ExpandStation: {ExpandStation}, - }, - } InternshipExpandConfig = ExpandConfig{ AllowedFields: []ExpandableField{ExpandCategory}, @@ -40,6 +34,13 @@ var ( ExpandCategory: {ExpandCategory}, }, } + + CartExpandConfig = ExpandConfig{ + AllowedFields: []ExpandableField{ExpandCartLineItems}, + NestedExpands: map[ExpandableField][]ExpandableField{ + ExpandCartLineItems: {ExpandCartLineItems}, + }, + } ) // Expand represents the expand parameter in API requests diff --git a/internal/types/internship.go b/internal/types/internship.go index 4dc0231..bb732bc 100644 --- a/internal/types/internship.go +++ b/internal/types/internship.go @@ -15,6 +15,22 @@ const ( InternshipModeOnsite InternshipMode = "onsite" ) +var InternshipModes = []InternshipMode{ + InternshipModeRemote, + InternshipModeHybrid, + InternshipModeOnsite, +} + +func (m InternshipMode) Validate() error { + if !lo.Contains(InternshipModes, m) { + return ierr.NewErrorf("invalid internship mode"). + WithReportableDetails(map[string]any{"mode": m}). + Mark(ierr.ErrValidation) + } + + return nil +} + type InternshipLevel string const ( @@ -23,18 +39,24 @@ const ( InternshipLevelAdvanced InternshipLevel = "advanced" ) -var InternshipModes = []InternshipMode{ - InternshipModeRemote, - InternshipModeHybrid, - InternshipModeOnsite, -} - var InternshipLevels = []InternshipLevel{ InternshipLevelBeginner, InternshipLevelIntermediate, InternshipLevelAdvanced, } +func (l InternshipLevel) Validate() error { + if !lo.Contains(InternshipLevels, l) { + return ierr.NewErrorf("invalid internship level"). + WithReportableDetails(map[string]any{"level": l}). + Mark(ierr.ErrValidation) + } + + return nil +} + + + type InternshipFilter struct { *QueryFilter *TimeRangeFilter diff --git a/internal/types/internship_enrollment.go b/internal/types/internship_enrollment.go new file mode 100644 index 0000000..5185048 --- /dev/null +++ b/internal/types/internship_enrollment.go @@ -0,0 +1,113 @@ +package types + +// lifecycle of enrollment +// pending -> enrolled -> completed -> refund +// +// -> failed +// -> cancelled +type InternshipEnrollmentStatus string + +const ( + InternshipEnrollmentStatusPending InternshipEnrollmentStatus = "pending" + InternshipEnrollmentStatusEnrolled InternshipEnrollmentStatus = "enrolled" + InternshipEnrollmentStatusCompleted InternshipEnrollmentStatus = "completed" + InternshipEnrollmentStatusRefunded InternshipEnrollmentStatus = "refunded" + InternshipEnrollmentStatusCancelled InternshipEnrollmentStatus = "cancelled" + InternshipEnrollmentStatusFailed InternshipEnrollmentStatus = "failed" +) + +type InternshipEnrollmentFilter struct { + *QueryFilter + *TimeRangeFilter + + InternshipIDs []string `json:"internship_ids,omitempty" form:"internship_ids" validate:"omitempty"` + UserID string `json:"user_id,omitempty" form:"user_id" validate:"omitempty"` + EnrollmentStatus InternshipEnrollmentStatus `json:"enrollment_status,omitempty" form:"enrollment_status" validate:"omitempty"` + PaymentStatus PaymentStatus `json:"payment_status,omitempty" form:"payment_status" validate:"omitempty"` + EnrollmentIDs []string `json:"enrollment_ids,omitempty" form:"enrollment_ids" validate:"omitempty"` + PaymentID *string `json:"payment_id,omitempty" form:"payment_id" validate:"omitempty"` + InternshipBatchID *string `json:"internship_batch_id,omitempty" form:"internship_batch_id" validate:"omitempty"` +} + +func (f *InternshipEnrollmentFilter) Validate() error { + if err := f.QueryFilter.Validate(); err != nil { + return err + } + + if err := f.TimeRangeFilter.Validate(); err != nil { + return err + } + + return nil +} + +// NewEnrollmentFilter creates a new EnrollmentFilter with default values +func NewInternshipEnrollmentFilter() *InternshipEnrollmentFilter { + return &InternshipEnrollmentFilter{ + QueryFilter: NewDefaultQueryFilter(), + TimeRangeFilter: &TimeRangeFilter{}, + } +} + +// NewNoLimitEnrollmentFilter creates a new EnrollmentFilter with no limit +func NewNoLimitInternshipEnrollmentFilter() *InternshipEnrollmentFilter { + return &InternshipEnrollmentFilter{ + QueryFilter: NewNoLimitQueryFilter(), + TimeRangeFilter: &TimeRangeFilter{}, + } +} + +// GetLimit returns the limit for the EnrollmentFilter +func (f *InternshipEnrollmentFilter) GetLimit() int { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetLimit() + } + return f.QueryFilter.GetLimit() +} + +// GetOffset returns the offset for the EnrollmentFilter +func (f *InternshipEnrollmentFilter) GetOffset() int { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetOffset() + } + return f.QueryFilter.GetOffset() +} + +// GetStatus returns the status for the EnrollmentFilter +func (f *InternshipEnrollmentFilter) GetStatus() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetStatus() + } + return f.QueryFilter.GetStatus() +} + +// GetSort implements BaseFilter interface +func (f *InternshipEnrollmentFilter) GetSort() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetSort() + } + return f.QueryFilter.GetSort() +} + +// GetOrder implements BaseFilter interface +func (f *InternshipEnrollmentFilter) GetOrder() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetOrder() + } + return f.QueryFilter.GetOrder() +} + +// GetExpand implements BaseFilter interface +func (f *InternshipEnrollmentFilter) GetExpand() Expand { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetExpand() + } + return f.QueryFilter.GetExpand() +} + +func (f *InternshipEnrollmentFilter) IsUnlimited() bool { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().IsUnlimited() + } + return f.QueryFilter.IsUnlimited() +} diff --git a/internal/types/internshipbatch.go b/internal/types/internshipbatch.go new file mode 100644 index 0000000..da707fe --- /dev/null +++ b/internal/types/internshipbatch.go @@ -0,0 +1,146 @@ +package types + +import ( + "time" + + ierr "github.com/omkar273/codegeeky/internal/errors" + "github.com/samber/lo" +) + +type InternshipBatchStatus string + +const ( + InternshipBatchStatusUpcoming InternshipBatchStatus = "upcoming" + InternshipBatchStatusOngoing InternshipBatchStatus = "ongoing" + InternshipBatchStatusCompleted InternshipBatchStatus = "completed" + InternshipBatchStatusCancelled InternshipBatchStatus = "cancelled" +) + +var InternshipBatchStatuses = []InternshipBatchStatus{ + InternshipBatchStatusUpcoming, + InternshipBatchStatusOngoing, + InternshipBatchStatusCompleted, + InternshipBatchStatusCancelled, +} + +func (s InternshipBatchStatus) Validate() error { + if !lo.Contains(InternshipBatchStatuses, s) { + return ierr.NewErrorf("invalid internship batch status"). + WithReportableDetails(map[string]any{"status": s}). + Mark(ierr.ErrValidation) + } + + return nil +} + +type InternshipBatchFilter struct { + *QueryFilter + *TimeRangeFilter + + // These fields are used to filter internships by internship id + InternshipIDs []string `json:"internship_ids,omitempty" form:"internship_ids" validate:"omitempty"` + + // These fields are used to filter internships by name + Name string `json:"name,omitempty" form:"name" validate:"omitempty"` + + // These fields are used to filter internships by start and end date + BatchStatus InternshipBatchStatus `json:"batch_status,omitempty" form:"batch_status" validate:"omitempty"` + StartDate *time.Time `json:"start_date,omitempty" form:"start_date" validate:"omitempty"` + EndDate *time.Time `json:"end_date,omitempty" form:"end_date" validate:"omitempty"` +} + +func (f *InternshipBatchFilter) Validate() error { + if err := f.QueryFilter.Validate(); err != nil { + return err + } + + if err := f.TimeRangeFilter.Validate(); err != nil { + return err + } + + if f.StartDate != nil && f.EndDate != nil && f.StartDate.After(*f.EndDate) { + return ierr.NewErrorf("start date must be before end date"). + WithReportableDetails(map[string]any{ + "start_date": f.StartDate, + "end_date": f.EndDate, + }). + Mark(ierr.ErrValidation) + } + + if f.BatchStatus != "" { + if err := f.BatchStatus.Validate(); err != nil { + return err + } + } + + return nil +} + +func NewInternshipBatchFilter() *InternshipBatchFilter { + return &InternshipBatchFilter{ + QueryFilter: NewDefaultQueryFilter(), + TimeRangeFilter: &TimeRangeFilter{}, + } +} + +func NewNoLimitInternshipBatchFilter() *InternshipBatchFilter { + return &InternshipBatchFilter{ + QueryFilter: NewNoLimitQueryFilter(), + TimeRangeFilter: &TimeRangeFilter{}, + } +} + +// GetLimit implements BaseFilter interface +func (f *InternshipBatchFilter) GetLimit() int { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetLimit() + } + return f.QueryFilter.GetLimit() +} + +// GetOffset implements BaseFilter interface +func (f *InternshipBatchFilter) GetOffset() int { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetOffset() + } + return f.QueryFilter.GetOffset() +} + +// GetStatus implements BaseFilter interface +func (f *InternshipBatchFilter) GetStatus() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetStatus() + } + return f.QueryFilter.GetStatus() +} + +// GetSort implements BaseFilter interface +func (f *InternshipBatchFilter) GetSort() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetSort() + } + return f.QueryFilter.GetSort() +} + +// GetOrder implements BaseFilter interface +func (f *InternshipBatchFilter) GetOrder() string { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetOrder() + } + return f.QueryFilter.GetOrder() +} + +// GetExpand implements BaseFilter interface +func (f *InternshipBatchFilter) GetExpand() Expand { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().GetExpand() + } + return f.QueryFilter.GetExpand() +} + +func (f *InternshipBatchFilter) IsUnlimited() bool { + if f.QueryFilter == nil { + return NewDefaultQueryFilter().IsUnlimited() + } + return f.QueryFilter.IsUnlimited() +} diff --git a/internal/types/payment.go b/internal/types/payment.go index b98bb25..d38c7e9 100644 --- a/internal/types/payment.go +++ b/internal/types/payment.go @@ -13,10 +13,11 @@ const ( PaymentStatusFailed PaymentStatus = "failed" PaymentStatusPendingRefund PaymentStatus = "pending_refund" PaymentStatusRefunding PaymentStatus = "refunding" - PaymentStatusRefunded PaymentStatus = "refunded" + PaymentStatusProcessing PaymentStatus = "processing" PaymentStatusPartiallyRefunded PaymentStatus = "partially_refunded" PaymentStatusCancelled PaymentStatus = "cancelled" PaymentStatusExpired PaymentStatus = "expired" + PaymentStatusRefunded PaymentStatus = "refunded" ) func (s PaymentStatus) String() string { @@ -103,6 +104,7 @@ func (s PaymentGatewayProvider) Validate() error { type PaymentDestinationType string const ( + PaymentDestinationTypeInternship PaymentDestinationType = "internship" PaymentDestinationTypeEnrollment PaymentDestinationType = "enrollment" PaymentDestinationTypeCourse PaymentDestinationType = "course" PaymentDestinationTypeOrder PaymentDestinationType = "order" @@ -110,6 +112,7 @@ const ( func (s PaymentDestinationType) Validate() error { allowed := []PaymentDestinationType{ + PaymentDestinationTypeInternship, PaymentDestinationTypeEnrollment, PaymentDestinationTypeCourse, PaymentDestinationTypeOrder, diff --git a/internal/types/uuid.go b/internal/types/uuid.go index 744d0b8..4540833 100644 --- a/internal/types/uuid.go +++ b/internal/types/uuid.go @@ -37,10 +37,17 @@ func ValidateUUIDWithPrefix(uuid string, prefix string) bool { const ( // Prefixes for all domains and entities - UUID_PREFIX_USER = "user" - UUID_PREFIX_INTERNSHIP = "internship" - UUID_PREFIX_CATEGORY = "category" - UUID_PREFIX_FILE_UPLOAD = "file" - UUID_PREFIX_PAYMENT = "payment" - UUID_PREFIX_DISCOUNT = "discount" + UUID_PREFIX_USER = "user" + UUID_PREFIX_INTERNSHIP = "internship" + UUID_PREFIX_CATEGORY = "category" + UUID_PREFIX_FILE_UPLOAD = "file" + UUID_PREFIX_PAYMENT = "payment" + UUID_PREFIX_DISCOUNT = "discount" + UUID_PREFIX_DISCOUNT_APPLIED = "da" + UUID_PREFIX_INTERNSHIP_ENROLLMENT = "ie" + UUID_PREFIX_PAYMENT_ATTEMPT = "pa" + UUID_PREFIX_INTERNSHIP_BATCH = "batch" + UUID_PREFIX_CART = "cart" + UUID_PREFIX_CART_LINE_ITEM = "cli" + UUID_PREFIX_INTERNSHIP_BATCH_ENROLLMENT = "enrollment" )