381 lines
15 KiB
Go
381 lines
15 KiB
Go
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"]
|
|
}
|