package company import ( "mime/multipart" "tm/internal/user" "tm/pkg/logger" "tm/pkg/response" "github.com/labstack/echo/v4" ) // Handler handles HTTP requests for company operations type Handler struct { service Service userHandler *user.Handler logger logger.Logger } // NewHandler creates a new company handler func NewHandler(service Service, userHandler *user.Handler, logger logger.Logger) *Handler { return &Handler{ service: service, userHandler: userHandler, logger: logger, } } // **************************** PUBLIC ENDPOINTS **************************** // MyCompany returns the authenticated user's company profile (Mobile App) // @Summary Get my company profile // @Description Retrieve the profile of the company associated with the authenticated user // @Tags Company // @Accept json // @Produce json // @Success 200 {object} response.APIResponse{data=CompanyProfileResponse} "Company retrieved successfully" // @Failure 401 {object} response.APIResponse "Unauthorized - User not authenticated" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /api/v1/companies [get] func (h *Handler) MyCompany(c echo.Context) error { companyID, err := user.GetCompanyIDFromContext(c) if err != nil { return response.Unauthorized(c, "User not authenticated") } company, err := h.service.GetMyCompany(c.Request().Context(), companyID) if err != nil { return response.InternalServerError(c, "Failed to get company") } return response.Success(c, company, "Company retrieved successfully") } // **************************** ADMIN ENDPOINTS **************************** // Create creates a new company (Web Panel) // @Summary Create a new company // @Description Create a new company with comprehensive information including business details, address, tags, and customer assignment. This endpoint is used by the web panel for full company registration. // @Tags Admin-Companies // @Accept json // @Produce json // @Param company body CompanyForm true "Company information including type, business details, address, tags, and optional customer assignment" // @Success 201 {object} response.APIResponse{data=CompanyResponse} "Company created successfully" // @Failure 400 {object} response.APIResponse "Bad request - Invalid input data" // @Failure 409 {object} response.APIResponse "Conflict - Company with this name/registration/tax ID already exists" // @Failure 422 {object} response.APIResponse "Validation error - Invalid request data" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /admin/v1/companies [post] func (h *Handler) Create(c echo.Context) error { form, err := response.Parse[CompanyForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) } company, err := h.service.Create(c.Request().Context(), form) if err != nil { if err.Error() == "company with this name already exists" || err.Error() == "company with this registration number already exists" || err.Error() == "company with this tax ID already exists" { return response.Conflict(c, err.Error()) } if err.Error() == "one or more categories are invalid" || err.Error() == "failed to validate categories" { return response.BadRequest(c, err.Error(), "Invalid category data") } return response.InternalServerError(c, "Failed to create company") } return response.Created(c, company, "Company created successfully") } // GetCompany retrieves a company by ID (Web Panel) // @Summary Get company by ID // @Description Retrieve detailed company information by company ID // @Tags Admin-Companies // @Accept json // @Produce json // @Param id path string true "Company ID" // @Success 200 {object} response.APIResponse{data=CompanyResponse} "Company retrieved successfully" // @Failure 400 {object} response.APIResponse "Bad request - Invalid company ID" // @Failure 404 {object} response.APIResponse "Not found - Company not found" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /admin/v1/companies/{id} [get] func (h *Handler) Get(c echo.Context) error { id := c.Param("id") company, err := h.service.GetByID(c.Request().Context(), id) if err != nil { if err.Error() == "company not found" { return response.NotFound(c, "Company not found") } return response.InternalServerError(c, "Failed to get company") } return response.Success(c, company, "Company retrieved successfully") } // Update updates a company (Web Panel) // @Summary Update company information // @Description Update company information including business details, address, tags, and customer assignment // @Tags Admin-Companies // @Accept json // @Produce json // @Param id path string true "Company ID" // @Param company body CompanyForm true "Company update information" // @Success 200 {object} response.APIResponse{data=CompanyResponse} "Company updated successfully" // @Failure 400 {object} response.APIResponse "Bad request - Invalid input data" // @Failure 404 {object} response.APIResponse "Not found - Company not found" // @Failure 409 {object} response.APIResponse "Conflict - Company with this name/registration/tax ID already exists" // @Failure 422 {object} response.APIResponse "Validation error - Invalid request data" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /admin/v1/companies/{id} [put] func (h *Handler) Update(c echo.Context) error { id := c.Param("id") form, err := response.Parse[CompanyForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) } company, err := h.service.Update(c.Request().Context(), id, form) if err != nil { if err.Error() == "company not found" { return response.NotFound(c, "Company not found") } if err.Error() == "company with this name already exists" || err.Error() == "company with this registration number already exists" || err.Error() == "company with this tax ID already exists" { return response.Conflict(c, err.Error()) } if err.Error() == "one or more categories are invalid" || err.Error() == "failed to validate categories" { return response.BadRequest(c, err.Error(), "Invalid category data") } return response.InternalServerError(c, "Failed to update company") } return response.Success(c, company, "Company updated successfully") } // Delete deletes a company (Web Panel) // @Summary Delete company // @Description Soft delete a company by setting status to inactive // @Tags Admin-Companies // @Accept json // @Produce json // @Param id path string true "Company ID" // @Success 200 {object} response.APIResponse "Company deleted successfully" // @Failure 400 {object} response.APIResponse "Bad request - Invalid company ID" // @Failure 404 {object} response.APIResponse "Not found - Company not found" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /admin/v1/companies/{id} [delete] func (h *Handler) Delete(c echo.Context) error { id := c.Param("id") err := h.service.Delete(c.Request().Context(), id) if err != nil { if err.Error() == "company not found" { return response.NotFound(c, "Company not found") } return response.InternalServerError(c, "Failed to delete company") } return response.Success(c, map[string]interface{}{ "message": "Company deleted successfully", }, "Company deleted successfully") } // Search searches companies with advanced filters (Web Panel) // @Summary Advanced search for companies // @Description Search companies with advanced filtering capabilities including tags, business criteria, and location // @Tags Admin-Companies // @Accept json // @Produce json // @Param q query string false "Search query" // @Param cpv_codes query array false "CPV codes filter" // @Param categories query array false "Categories filter" // @Param keywords query array false "Keywords filter" // @Param specializations query array false "Specializations filter" // @Param industry query string false "Industry filter" // @Param location query string false "Location filter" // @Param min_employees query integer false "Minimum employees" // @Param max_employees query integer false "Maximum employees" // @Param min_revenue query number false "Minimum revenue" // @Param max_revenue query number false "Maximum revenue" // @Param is_verified query boolean false "Verification status" // @Param is_compliant query boolean false "Compliance status" // @Param limit query integer false "Limit" default(20) // @Param offset query integer false "Offset" default(0) // @Param sort_by query string false "Field to sort by" Enums(name, type, industry, created_at, updated_at, status, employee_count, annual_revenue, email, phone, country, state, language, currency) default(created_at) // @Param sort_order query string false "Sort order" Enums(asc, desc) default(desc) // @Success 200 {object} response.APIResponse{data=CompanyListResponse} "Companies search completed" // @Failure 400 {object} response.APIResponse "Bad request - Invalid query parameters" // @Failure 422 {object} response.APIResponse "Validation error - Invalid query parameters" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /admin/v1/companies [get] func (h *Handler) Search(c echo.Context) error { form, err := response.Parse[SearchForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) } pagination, err := response.NewPagination(c) if err != nil { return response.PaginationBadRequest(c, err) } result, err := h.service.SearchCompanies(c.Request().Context(), form, pagination) if err != nil { if response.IsListPaginationError(err) { return response.PaginationBadRequest(c, err) } return response.InternalServerError(c, "Failed to list companies") } return response.SuccessWithMeta(c, result, result.Meta, "Companies retrieved successfully") } // UpdateStatus updates company status (Web Panel) // @Summary Update company status // @Description Update company account status (active, inactive, suspended, pending) // @Tags Admin-Companies // @Accept json // @Produce json // @Param id path string true "Company ID" // @Param status body StatusForm true "New company status" // @Success 200 {object} response.APIResponse "Company status updated successfully" // @Failure 400 {object} response.APIResponse "Bad request - Invalid input data" // @Failure 404 {object} response.APIResponse "Not found - Company not found" // @Failure 422 {object} response.APIResponse "Validation error - Invalid request data" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /admin/v1/companies/{id}/status [patch] func (h *Handler) UpdateStatus(c echo.Context) error { id := c.Param("id") form, err := response.Parse[StatusForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) } err = h.service.UpdateStatus(c.Request().Context(), id, form) if err != nil { if err.Error() == "company not found" { return response.NotFound(c, "Company not found") } return response.InternalServerError(c, "Failed to update company status") } return response.Success(c, map[string]interface{}{ "message": "Company status updated successfully", }, "Company status updated successfully") } // UpdateVerification updates company verification status (Web Panel) // @Summary Update company verification status // @Description Update company verification and compliance status // @Tags Admin-Companies // @Accept json // @Produce json // @Param id path string true "Company ID" // @Param verification body VerificationForm true "Verification status update" // @Success 200 {object} response.APIResponse "Company verification updated successfully" // @Failure 400 {object} response.APIResponse "Bad request - Invalid input data" // @Failure 404 {object} response.APIResponse "Not found - Company not found" // @Failure 422 {object} response.APIResponse "Validation error - Invalid request data" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /admin/v1/companies/{id}/verification [patch] func (h *Handler) UpdateVerification(c echo.Context) error { id := c.Param("id") form, err := response.Parse[VerificationForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) } err = h.service.UpdateVerification(c.Request().Context(), id, form) if err != nil { if err.Error() == "company not found" { return response.NotFound(c, "Company not found") } return response.InternalServerError(c, "Failed to update company verification") } return response.Success(c, map[string]interface{}{ "message": "Company verification updated successfully", }, "Company verification updated successfully") } // UploadDocuments uploads multiple documents for a company (Web Panel) // @Summary Upload company documents // @Description Upload one or more files and attach them to the company (max 20 files per request). Use form field "files" for multiple files. // @Tags Admin-Companies // @Accept multipart/form-data // @Produce json // @Param id path string true "Company ID" // @Param files formData file true "Files to upload (repeat field for multiple files)" // @Success 200 {object} response.APIResponse{data=UploadDocumentsResponse} "Documents uploaded successfully" // @Failure 400 {object} response.APIResponse "Bad request - No files or all uploads failed" // @Failure 404 {object} response.APIResponse "Not found - Company not found" // @Failure 422 {object} response.APIResponse "Validation error - Too many files" // @Failure 500 {object} response.APIResponse "Internal server error" // @Security BearerAuth // @Router /admin/v1/companies/{id}/documents [post] func (h *Handler) UploadDocuments(c echo.Context) error { id := c.Param("id") userID, err := user.GetUserIDFromContext(c) if err != nil { return response.Unauthorized(c, "User not authenticated") } form, err := c.MultipartForm() if err != nil { return response.ValidationError(c, "Invalid multipart form", err.Error()) } files := collectCompanyUploadFiles(form) if len(files) == 0 { return response.BadRequest(c, "No files provided", "Use form field \"files\" with one or more files") } if len(files) > maxCompanyDocumentsUpload { return response.ValidationError(c, "Too many files", "Maximum 20 files per request") } result, err := h.service.UploadDocuments(c.Request().Context(), id, userID, files) if err != nil { switch err.Error() { case "company not found": return response.NotFound(c, "Company not found") case "no files provided", "too many files in request": return response.BadRequest(c, err.Error(), "") case "all file uploads failed": return response.BadRequest(c, "All file uploads failed", "") default: return response.InternalServerError(c, "Failed to upload company documents") } } message := "Company documents uploaded successfully" if len(result.Failed) > 0 { message = "Some company documents uploaded successfully" } return response.Success(c, result, message) } func collectCompanyUploadFiles(form *multipart.Form) []*multipart.FileHeader { if form == nil { return nil } if files := form.File["files"]; len(files) > 0 { return files } return form.File["file"] }