feat: Add company model and API with tests (#12548)

# Pull Request Template

## Description

* add Company model with validations for name, domain, description and
  avatar
* Add database migration fo
* Implement endpoints for company CRUD operations
* Add optional company relationship for contacts
* Add test for models, controllers, factories and policies
* Add authorization policies restricting delete to admins
* support JSON API responses
Please include a summary of the change and issue(s) fixed. Also, mention
relevant motivation, context, and any dependencies that this change
requires.

Fixes #(cw-5650)

## Type of change

Please delete options that are not relevant.

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality not to work as expected)
- [ ] This change requires a documentation update

## How Has This Been Tested?

Tests are implemented using `RSpec`

```
$ bundle exec rails db:migrate
$ bundle exec rspec spec/models/company_spec.rb spec/controllers/api/v1/accounts/companies_controller_spec.rb
```

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
This commit is contained in:
Vinay Keerthi
2025-10-08 20:23:43 +05:30
committed by GitHub
parent 606adffeeb
commit 170ea7691f
23 changed files with 398 additions and 2 deletions

View File

@@ -0,0 +1,40 @@
class Api::V1::Accounts::CompaniesController < Api::V1::Accounts::EnterpriseAccountsController
before_action :check_authorization
before_action :fetch_company, only: [:show, :update, :destroy]
def index
@companies = Current.account.companies.ordered_by_name
end
def show; end
def create
@company = Current.account.companies.build(company_params)
@company.save!
end
def update
@company.update!(company_params)
end
def destroy
@company.destroy!
head :ok
end
private
def check_authorization
raise Pundit::NotAuthorizedError unless ChatwootApp.enterprise?
authorize(Company)
end
def fetch_company
@company = Current.account.companies.find(params[:id])
end
def company_params
params.require(:company).permit(:name, :domain, :description, :avatar)
end
end

View File

@@ -0,0 +1,33 @@
# == Schema Information
#
# Table name: companies
#
# id :bigint not null, primary key
# description :text
# domain :string
# name :string not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint not null
#
# Indexes
#
# index_companies_on_account_id (account_id)
# index_companies_on_domain_and_account_id (domain,account_id)
# index_companies_on_name_and_account_id (name,account_id)
#
class Company < ApplicationRecord
include Avatarable
validates :account_id, presence: true
validates :name, presence: true, length: { maximum: Limits::COMPANY_NAME_LENGTH_LIMIT }
validates :domain, allow_blank: true, format: {
with: /\A[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)+\z/,
message: I18n.t('errors.companies.domain.invalid')
}
validates :description, length: { maximum: Limits::COMPANY_DESCRIPTION_LENGTH_LIMIT }
belongs_to :account
has_many :contacts, dependent: :nullify
scope :ordered_by_name, -> { order(:name) }
end

View File

@@ -13,6 +13,7 @@ module Enterprise::Concerns::Account
has_many :captain_custom_tools, dependent: :destroy_async, class_name: 'Captain::CustomTool'
has_many :copilot_threads, dependent: :destroy_async
has_many :companies, dependent: :destroy_async
has_many :voice_channels, dependent: :destroy_async, class_name: '::Channel::Voice'
has_one :saml_settings, dependent: :destroy_async, class_name: 'AccountSamlSettings'

View File

@@ -0,0 +1,6 @@
module Enterprise::Concerns::Contact
extend ActiveSupport::Concern
included do
belongs_to :company, optional: true
end
end

View File

@@ -0,0 +1,21 @@
class CompanyPolicy < ApplicationPolicy
def index?
true
end
def show?
true
end
def create?
true
end
def update?
true
end
def destroy?
@account_user.administrator?
end
end

View File

@@ -0,0 +1,7 @@
json.id company.id
json.name company.name
json.domain company.domain
json.description company.description
json.avatar_url company.avatar_url
json.created_at company.created_at
json.updated_at company.updated_at

View File

@@ -0,0 +1,3 @@
json.payload do
json.partial! 'company', company: @company
end

View File

@@ -0,0 +1,5 @@
json.payload do
json.array! @companies do |company|
json.partial! 'company', company: company
end
end

View File

@@ -0,0 +1,3 @@
json.payload do
json.partial! 'company', company: @company
end

View File

@@ -0,0 +1,3 @@
json.payload do
json.partial! 'company', company: @company
end