module Ecom module Core class TakeoffCalculator < ApplicationRecord INPUT_PARAMETERS_SCHEMA = { 'type' => 'object', 'required' => %w[name label input_type], 'additionalProperties' => false, 'properties' => { 'name' => { 'type' => 'string' }, 'label' => { 'type' => 'string' }, 'input_type' => { 'type' => 'string', 'enum' => %w[string number measurement enum] } }, "patternProperties": { "enum_values": { 'type' => 'array' }, "default_value": { 'type' => 'object', 'required' => %w[value], 'properties' => { 'value' => { 'type' => 'string' } }, "patternProperties": { "measurement_unit_id": { 'type' => 'integer' } } }, "validation": { 'type' => 'object', 'required' => %w[required], 'properties' => { 'required' => { 'type' => 'boolean' } }, "patternProperties": { "minimum": { 'type' => 'number' }, "maximum": { 'type' => 'number' } } } } }.freeze REBAR_TAKEOFF_CALCULATOR = 'Rebar Takeoff Calculator'.freeze FORMWORK_TAKEOFF_CALCULATOR = 'Formwork Takeoff Calculator'.freeze CONCRETE_POURING_TAKEOFF_CALCULATOR = 'Concrete Pouring Takeoff Calculator'.freeze TAKEOFF_CALCULATOR_NAMES = [ REBAR_TAKEOFF_CALCULATOR, FORMWORK_TAKEOFF_CALCULATOR, CONCRETE_POURING_TAKEOFF_CALCULATOR ].freeze validates :name, :input_parameters, presence: true validates :name, inclusion: TAKEOFF_CALCULATOR_NAMES validates :name, uniqueness: true validate :input_parameters_schema validate :measurement_unit_id_in_input_parameters validate :enum_in_input_parameters def input_parameters_schema return if input_parameters.nil? errors.add(:input_parameters, 'There has to be one or more input parameters') if input_parameters.count.zero? validation_errors = JSON::Validator.fully_validate(INPUT_PARAMETERS_SCHEMA, input_parameters, strict: true, list: true, clear_cache: true) errors.add(:input_parameters, validation_errors) unless validation_errors.empty? end def measurement_unit_id_in_input_parameters return if input_parameters.nil? input_parameters.each do |input_parameter| next unless input_parameter['input_type'] == 'measurement' && !input_parameter['default_value'].nil? if input_parameter['default_value']['measurement_unit_id'].nil? errors.add(:base, 'measurement_unit_id is required') next end measurement_unit_id = input_parameter['default_value']['measurement_unit_id'] measurement_unit = Ecom::Core::MeasurementUnit.find_by(id: measurement_unit_id) errors.add(:base, "measurement unit with id #{measurement_unit_id} does not exist") if measurement_unit.nil? end end def enum_in_input_parameters return if input_parameters.nil? input_parameters.each do |input_parameter| next if input_parameter['input_type'] != 'enum' if input_parameter['enum_values'].nil? errors.add(:base, 'enum_values are requiered if the input_type is enum') next end next unless !input_parameter['enum_values'].nil? && input_parameter['enum_values'].class == Array && input_parameter['enum_values'].count.zero? errors.add(:base, 'Provide at least one enum value') end end end end end