1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-19 13:54:25 +01:00

WIP: interface to configure packs

This commit is contained in:
Sylvain 2021-06-21 17:39:48 +02:00
parent 7ac60f6ef3
commit d54f30e048
23 changed files with 449 additions and 55 deletions

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
# API Controller for resources of type PrepaidPack
# PrepaidPacks are used to provide discounts to users that bought many hours at once
class API::PrepaidPacksController < API::ApiController
before_action :authenticate_user!, except: :index
before_action :set_pack, only: %i[show update destroy]
def index
@packs = PrepaidPackService.list(params)
end
def show; end
def create
authorize PrepaidPack
@pack = PrepaidPack.new(pack_params)
if @pack.save
render status: :created
else
render json: @pack.errors.full_messages, status: :unprocessable_entity
end
end
def update
authorize @pack
if @pack.update(pack_params)
render status: :ok
else
render json: @pack.errors.full_messages, status: :unprocessable_entity
end
end
def destroy
authorize @pack
@pack.destroy
head :no_content
end
private
def set_pack
@pack = PrepaidPack.find(params[:id])
end
def pack_params
pack_params = params
pack_params[:pack][:amount] = pack_params[:pack][:amount].to_f * 100.0 if pack_params[:pack][:amount]
params.require(:pack).permit(:priceable_id, :priceable_type, :group_id, :amount, :minutes)
end
end

View File

@ -0,0 +1,38 @@
import apiClient from './clients/api-client';
import { AxiosResponse } from 'axios';
import { IndexFilter, PrepaidPack } from '../models/prepaid-pack';
export default class PrepaidPackAPI {
static async index (filters?: Array<IndexFilter>): Promise<Array<PrepaidPack>> {
const res: AxiosResponse<Array<PrepaidPack>> = await apiClient.get(`/api/prepaid_packs${PrepaidPackAPI.filtersToQuery(filters)}`);
return res?.data;
}
static async get (id: number): Promise<PrepaidPack> {
const res: AxiosResponse<PrepaidPack> = await apiClient.get(`/api/prepaid_packs/${id}`);
return res?.data;
}
static async create (pack: PrepaidPack): Promise<PrepaidPack> {
const res: AxiosResponse<PrepaidPack> = await apiClient.post('/api/prepaid_packs', { pack });
return res?.data;
}
static async update (pack: PrepaidPack): Promise<PrepaidPack> {
const res: AxiosResponse<PrepaidPack> = await apiClient.patch(`/api/prepaid_packs/${pack.id}`, { pack });
return res?.data;
}
static async destroy (packId: number): Promise<void> {
const res: AxiosResponse<void> = await apiClient.delete(`/api/prepaid_packs/${packId}`);
return res?.data;
}
private static filtersToQuery(filters?: Array<IndexFilter>): string {
if (!filters) return '';
return '?' + filters.map(f => `${f.key}=${f.value}`).join('&');
}
}

View File

@ -1,9 +1,65 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import PrepaidPackAPI from '../api/prepaid-pack';
import { IndexFilter, PrepaidPack } from '../models/prepaid-pack';
import { Loader } from './base/loader';
import { react2angular } from 'react2angular';
import { IApplication } from '../models/application';
declare var Application: IApplication;
interface ConfigurePacksButtonParams {
groupId: number,
priceableId: number,
priceableType: string,
onError: (message: string) => void,
}
/**
* This component is a button that shows the list of prepaid-packs when moving the mouse over it.
* When clicked, it opens a modal dialog to configure (add/delete/edit/remove) prepaid-packs.
*/
const ConfigurePacksButton: React.FC = ({}) => {
return <button/>;
const ConfigurePacksButton: React.FC<ConfigurePacksButtonParams> = ({ groupId, priceableId, priceableType, onError }) => {
const [packs, setPacks] = useState<Array<PrepaidPack>>(null);
const [showList, setShowList] = useState<boolean>(false);
useEffect(() => {
PrepaidPackAPI.index(buildFilters())
.then(data => setPacks(data))
.catch(error => onError(error))
}, [])
/**
* Build the filters for the current ConfigurePackButton, to query the API and get the concerned packs.
*/
const buildFilters = (): Array<IndexFilter> => {
const res = [];
if (groupId) res.push({ key: 'group_id', value: groupId });
if (priceableId) res.push({ key: 'priceable_id', value: priceableId });
if (priceableType) res.push({ key: 'priceable_type', value: priceableType });
return res;
}
const toggleShowList = (): void => {
setShowList(!showList);
}
return (
<div className="configure-packs-button" onMouseOver={toggleShowList}>
{packs && showList && <div className="packs-overview">
{packs.map(p => <div>{p.minutes / 60}h - {p.amount}</div>)}
</div>}
</div>
);
}
const ConfigurePacksButtonWrapper: React.FC<ConfigurePacksButtonParams> = ({ groupId, priceableId, priceableType, onError }) => {
return (
<Loader>
<ConfigurePacksButton groupId={groupId} priceableId={priceableId} priceableType={priceableType} onError={onError}/>
</Loader>
);
}
Application.Components.component('configurePacksButton', react2angular(ConfigurePacksButtonWrapper, ['groupId', 'priceableId', 'priceableType', 'onError']));

View File

@ -0,0 +1,17 @@
export interface IndexFilter {
key: 'group_id' | 'priceable_id' | 'priceable_type',
value: number|string,
}
export interface PrepaidPack {
id?: number,
priceable_id: string,
priceable_type: string,
group_id: number,
validity_interval?: 'day' | 'week' | 'month' | 'year',
validity_count?: number,
minutes: number,
amount: number,
usages?: number,
}

View File

@ -23,7 +23,8 @@
onbeforesave="updatePrice($data, findPriceBy(machinesPrices, machine.id, group.id))">
{{ findPriceBy(machinesPrices, machine.id, group.id).amount | currency}}
</span>
</td>
<configure-packs-button group-id="group.id" priceable-id="machine.id" priceable-type="'Machine'" />
</td> <!-- FIXME: too much API calls -->
</tr>
</tbody>
</table>

View File

@ -1,26 +1,24 @@
# frozen_string_literal: true
# Prepaid-packs of hours for machines/spaces.
# A prepaid-pack is a set a hours that can be bought by a member. The member will be able to book for free as much hours
# as there's in the pack, after having bought one.
# The number of hours in each packs is saved in minutes
#
# A prepaid-pack is a set a hours that can be bought by a member. After having bought one, a member will be able to book, for free,
# as much hours as there is in the pack, until the validity has not expired.
#
# The number of hours in a pack is stored in minutes.
class PrepaidPack < ApplicationRecord
belongs_to :priceable, polymorphic: true
belongs_to :group
has_many :user_prepaid_packs
has_many :statistic_profile_prepaid_packs
validates :amount, :group_id, :priceable_id, :priceable_type, :minutes, presence: true
def hours
minutes / 60.0
def validity
validity_count.send(validity_interval)
end
def safe_destroy
if user_prepaid_packs.count.zero?
destroy
else
false
end
def destroyable?
statistic_profile_prepaid_packs.empty?
end
end

View File

@ -17,6 +17,10 @@ class StatisticProfile < ApplicationRecord
has_many :reservations, dependent: :destroy
accepts_nested_attributes_for :reservations, allow_destroy: false
# bought packs
has_many :statistic_profile_prepaid_packs, dependent: :destroy
has_many :prepaid_packs, through: :statistic_profile_prepaid_packs
# Trainings that were validated by an admin
has_many :statistic_profile_trainings, dependent: :destroy
has_many :trainings, through: :statistic_profile_trainings

View File

@ -2,8 +2,7 @@
# Associate a customer with a bought prepaid-packs of hours for machines/spaces.
# Also saves the amount of hours used
class UserPrepaidPack < ApplicationRecord
class StatisticProfilePrepaidPack < ApplicationRecord
belongs_to :prepaid_pack
belongs_to :user
belongs_to :statistic_profile
end

View File

@ -38,9 +38,6 @@ class User < ApplicationRecord
has_many :training_credits, through: :users_credits, source: :training_credit
has_many :machine_credits, through: :users_credits, source: :machine_credit
has_many :user_prepaid_packs, dependent: :destroy
has_many :prepaid_packs, through: :user_prepaid_packs
has_many :user_tags, dependent: :destroy
has_many :tags, through: :user_tags
accepts_nested_attributes_for :tags, allow_destroy: true
@ -71,6 +68,7 @@ class User < ApplicationRecord
delegate :reservations, to: :statistic_profile
delegate :trainings, to: :statistic_profile
delegate :my_projects, to: :statistic_profile
delegate :prepaid_packs, to: :statistic_profile
delegate :wallet, to: :invoicing_profile
delegate :wallet_transactions, to: :invoicing_profile
delegate :invoices, to: :invoicing_profile

View File

@ -11,6 +11,6 @@ class GroupPolicy < ApplicationPolicy
end
def destroy?
user.admin? and record.destroyable?
user.admin? && record.destroyable?
end
end

View File

@ -1,3 +1,6 @@
# frozen_string_literal: true
# Check the access policies for API::MachinesController
class MachinePolicy < ApplicationPolicy
def create?
user.admin?

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
# Check the access policies for API::PrepaidPacksController
class PrepaidPackPolicy < ApplicationPolicy
def create?
user.admin?
end
def update?
user.admin?
end
def destroy?
user.admin? && record.destroyable?
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
# Provides methods for PrepaidPack
class PrepaidPackService
def self.list(filters)
packs = PrepaidPack.where(nil)
packs = packs.where(group_id: filters[:group_id]) if filters[:group_id].present?
packs = packs.where(priceable_id: filters[:priceable_id]) if filters[:priceable_id].present?
packs = packs.where(priceable_type: filters[:priceable_type]) if filters[:priceable_type].present?
packs
end
end

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
json.extract! pack, :id, :priceable_id, :priceable_type, :group_id, :validity_interval, :validity_count, :minutes
json.amount pack.amount / 100.0
json.usages pack.statistic_profile_prepaid_packs.count

View File

@ -0,0 +1,3 @@
# frozen_string_literal: true
json.partial! 'api/prepaid_packs/pack', pack: @pack

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
json.array! @packs do |pack|
json.extract! pack, :priceable_id, :priceable_type, :group_id, :minutes
json.amount pack.amount / 100.0
end

View File

@ -0,0 +1,3 @@
# frozen_string_literal: true
json.partial! 'api/prepaid_packs/pack', pack: @pack

View File

@ -0,0 +1,3 @@
# frozen_string_literal: true
json.partial! 'api/prepaid_packs/pack', pack: @pack

View File

@ -78,6 +78,7 @@ Rails.application.routes.draw do
resources :prices, only: %i[index update] do
post 'compute', on: :collection
end
resources :prepaid_packs
resources :coupons do
post 'validate', action: 'validate', on: :collection
post 'send', action: 'send_to', on: :collection

View File

@ -8,6 +8,8 @@ class CreatePrepaidPacks < ActiveRecord::Migration[5.2]
t.references :group, foreign_key: true
t.integer :amount
t.integer :minutes
t.string :validity_interval
t.integer :validity_count
t.timestamps
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
# Bought and consumed prepaid-packs
class CreateStatisticProfilePrepaidPacks < ActiveRecord::Migration[5.2]
def change
create_table :statistic_profile_prepaid_packs do |t|
t.references :prepaid_pack, foreign_key: true
t.references :statistic_profile, foreign_key: true
t.integer :minutes_used, default: 0
t.datetime :expires_at
t.timestamps
end
end
end

View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
# Bought and consumed prepaid-packs
class CreateUserPrepaidPacks < ActiveRecord::Migration[5.2]
def change
create_table :user_prepaid_packs do |t|
t.references :prepaid_pack, foreign_key: true
t.references :user, foreign_key: true
t.integer :minutes_used, default: 0
t.timestamps
end
end
end

View File

@ -108,8 +108,8 @@ SET default_tablespace = '';
CREATE TABLE public.abuses (
id integer NOT NULL,
signaled_type character varying,
signaled_id integer,
signaled_type character varying,
first_name character varying,
last_name character varying,
email character varying,
@ -187,8 +187,8 @@ CREATE TABLE public.addresses (
locality character varying,
country character varying,
postal_code character varying,
placeable_type character varying,
placeable_id integer,
placeable_type character varying,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@ -263,8 +263,8 @@ CREATE TABLE public.ar_internal_metadata (
CREATE TABLE public.assets (
id integer NOT NULL,
viewable_type character varying,
viewable_id integer,
viewable_type character varying,
attachment character varying,
type character varying,
created_at timestamp without time zone,
@ -504,8 +504,8 @@ ALTER SEQUENCE public.coupons_id_seq OWNED BY public.coupons.id;
CREATE TABLE public.credits (
id integer NOT NULL,
creditable_type character varying,
creditable_id integer,
creditable_type character varying,
plan_id integer,
hours integer,
created_at timestamp without time zone,
@ -1223,15 +1223,15 @@ ALTER SEQUENCE public.machines_id_seq OWNED BY public.machines.id;
CREATE TABLE public.notifications (
id integer NOT NULL,
receiver_id integer,
attached_object_type character varying,
attached_object_id integer,
attached_object_type character varying,
notification_type_id integer,
is_read boolean DEFAULT false,
created_at timestamp without time zone,
updated_at timestamp without time zone,
receiver_type character varying,
is_send boolean DEFAULT false,
meta_data jsonb DEFAULT '"{}"'::jsonb
meta_data jsonb DEFAULT '{}'::jsonb
);
@ -1714,6 +1714,43 @@ CREATE SEQUENCE public.plans_id_seq
ALTER SEQUENCE public.plans_id_seq OWNED BY public.plans.id;
--
-- Name: prepaid_packs; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.prepaid_packs (
id bigint NOT NULL,
priceable_type character varying,
priceable_id bigint,
group_id bigint,
amount integer,
minutes integer,
validity_interval character varying,
validity_count integer,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
--
-- Name: prepaid_packs_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.prepaid_packs_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: prepaid_packs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.prepaid_packs_id_seq OWNED BY public.prepaid_packs.id;
--
-- Name: price_categories; Type: TABLE; Schema: public; Owner: -
--
@ -1754,8 +1791,8 @@ CREATE TABLE public.prices (
id integer NOT NULL,
group_id integer,
plan_id integer,
priceable_type character varying,
priceable_id integer,
priceable_type character varying,
amount integer,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
@ -2070,8 +2107,8 @@ CREATE TABLE public.reservations (
message text,
created_at timestamp without time zone,
updated_at timestamp without time zone,
reservable_type character varying,
reservable_id integer,
reservable_type character varying,
nb_reserve_places integer,
statistic_profile_id integer
);
@ -2103,8 +2140,8 @@ ALTER SEQUENCE public.reservations_id_seq OWNED BY public.reservations.id;
CREATE TABLE public.roles (
id integer NOT NULL,
name character varying,
resource_type character varying,
resource_id integer,
resource_type character varying,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@ -2441,6 +2478,40 @@ CREATE SEQUENCE public.statistic_indices_id_seq
ALTER SEQUENCE public.statistic_indices_id_seq OWNED BY public.statistic_indices.id;
--
-- Name: statistic_profile_prepaid_packs; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.statistic_profile_prepaid_packs (
id bigint NOT NULL,
prepaid_pack_id bigint,
statistic_profile_id bigint,
minutes_used integer DEFAULT 0,
expires_at timestamp without time zone,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
--
-- Name: statistic_profile_prepaid_packs_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.statistic_profile_prepaid_packs_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: statistic_profile_prepaid_packs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.statistic_profile_prepaid_packs_id_seq OWNED BY public.statistic_profile_prepaid_packs.id;
--
-- Name: statistic_profile_trainings; Type: TABLE; Schema: public; Owner: -
--
@ -3417,6 +3488,13 @@ ALTER TABLE ONLY public.plans ALTER COLUMN id SET DEFAULT nextval('public.plans_
ALTER TABLE ONLY public.plans_availabilities ALTER COLUMN id SET DEFAULT nextval('public.plans_availabilities_id_seq'::regclass);
--
-- Name: prepaid_packs id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.prepaid_packs ALTER COLUMN id SET DEFAULT nextval('public.prepaid_packs_id_seq'::regclass);
--
-- Name: price_categories id; Type: DEFAULT; Schema: public; Owner: -
--
@ -3564,6 +3642,13 @@ ALTER TABLE ONLY public.statistic_graphs ALTER COLUMN id SET DEFAULT nextval('pu
ALTER TABLE ONLY public.statistic_indices ALTER COLUMN id SET DEFAULT nextval('public.statistic_indices_id_seq'::regclass);
--
-- Name: statistic_profile_prepaid_packs id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.statistic_profile_prepaid_packs ALTER COLUMN id SET DEFAULT nextval('public.statistic_profile_prepaid_packs_id_seq'::regclass);
--
-- Name: statistic_profile_trainings id; Type: DEFAULT; Schema: public; Owner: -
--
@ -4073,6 +4158,14 @@ ALTER TABLE ONLY public.plans
ADD CONSTRAINT plans_pkey PRIMARY KEY (id);
--
-- Name: prepaid_packs prepaid_packs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.prepaid_packs
ADD CONSTRAINT prepaid_packs_pkey PRIMARY KEY (id);
--
-- Name: price_categories price_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -4169,14 +4262,6 @@ ALTER TABLE ONLY public.roles
ADD CONSTRAINT roles_pkey PRIMARY KEY (id);
--
-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.schema_migrations
ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
--
-- Name: settings settings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -4249,6 +4334,14 @@ ALTER TABLE ONLY public.statistic_indices
ADD CONSTRAINT statistic_indices_pkey PRIMARY KEY (id);
--
-- Name: statistic_profile_prepaid_packs statistic_profile_prepaid_packs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.statistic_profile_prepaid_packs
ADD CONSTRAINT statistic_profile_prepaid_packs_pkey PRIMARY KEY (id);
--
-- Name: statistic_profile_trainings statistic_profile_trainings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -4786,6 +4879,20 @@ CREATE INDEX index_plans_on_group_id ON public.plans USING btree (group_id);
CREATE INDEX index_plans_on_plan_category_id ON public.plans USING btree (plan_category_id);
--
-- Name: index_prepaid_packs_on_group_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_prepaid_packs_on_group_id ON public.prepaid_packs USING btree (group_id);
--
-- Name: index_prepaid_packs_on_priceable_type_and_priceable_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_prepaid_packs_on_priceable_type_and_priceable_id ON public.prepaid_packs USING btree (priceable_type, priceable_id);
--
-- Name: index_prices_on_group_id; Type: INDEX; Schema: public; Owner: -
--
@ -4989,6 +5096,20 @@ CREATE INDEX index_statistic_fields_on_statistic_index_id ON public.statistic_fi
CREATE INDEX index_statistic_graphs_on_statistic_index_id ON public.statistic_graphs USING btree (statistic_index_id);
--
-- Name: index_statistic_profile_prepaid_packs_on_prepaid_pack_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_statistic_profile_prepaid_packs_on_prepaid_pack_id ON public.statistic_profile_prepaid_packs USING btree (prepaid_pack_id);
--
-- Name: index_statistic_profile_prepaid_packs_on_statistic_profile_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_statistic_profile_prepaid_packs_on_statistic_profile_id ON public.statistic_profile_prepaid_packs USING btree (statistic_profile_id);
--
-- Name: index_statistic_profile_trainings_on_statistic_profile_id; Type: INDEX; Schema: public; Owner: -
--
@ -5276,6 +5397,29 @@ CREATE INDEX profiles_lower_unaccent_last_name_trgm_idx ON public.profiles USING
CREATE INDEX projects_search_vector_idx ON public.projects USING gin (search_vector);
--
-- Name: unique_schema_migrations; Type: INDEX; Schema: public; Owner: -
--
CREATE UNIQUE INDEX unique_schema_migrations ON public.schema_migrations USING btree (version);
--
-- Name: accounting_periods accounting_periods_del_protect; Type: RULE; Schema: public; Owner: -
--
CREATE RULE accounting_periods_del_protect AS
ON DELETE TO public.accounting_periods DO INSTEAD NOTHING;
--
-- Name: accounting_periods accounting_periods_upd_protect; Type: RULE; Schema: public; Owner: -
--
CREATE RULE accounting_periods_upd_protect AS
ON UPDATE TO public.accounting_periods DO INSTEAD NOTHING;
--
-- Name: projects projects_search_content_trigger; Type: TRIGGER; Schema: public; Owner: -
--
@ -5499,6 +5643,14 @@ ALTER TABLE ONLY public.payment_schedule_objects
ADD CONSTRAINT fk_rails_56f6b6d2d2 FOREIGN KEY (payment_schedule_id) REFERENCES public.payment_schedules(id);
--
-- Name: statistic_profile_prepaid_packs fk_rails_5af0f4258a; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.statistic_profile_prepaid_packs
ADD CONSTRAINT fk_rails_5af0f4258a FOREIGN KEY (statistic_profile_id) REFERENCES public.statistic_profiles(id);
--
-- Name: tickets fk_rails_65422fe751; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@ -5507,6 +5659,14 @@ ALTER TABLE ONLY public.tickets
ADD CONSTRAINT fk_rails_65422fe751 FOREIGN KEY (reservation_id) REFERENCES public.reservations(id);
--
-- Name: prepaid_packs fk_rails_6ea2aaae74; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.prepaid_packs
ADD CONSTRAINT fk_rails_6ea2aaae74 FOREIGN KEY (group_id) REFERENCES public.groups(id);
--
-- Name: spaces_availabilities fk_rails_6f123023fd; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@ -5635,6 +5795,14 @@ ALTER TABLE ONLY public.projects_themes
ADD CONSTRAINT fk_rails_b021a22658 FOREIGN KEY (theme_id) REFERENCES public.themes(id);
--
-- Name: statistic_profile_prepaid_packs fk_rails_b0251cdfcf; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.statistic_profile_prepaid_packs
ADD CONSTRAINT fk_rails_b0251cdfcf FOREIGN KEY (prepaid_pack_id) REFERENCES public.prepaid_packs(id);
--
-- Name: statistic_profiles fk_rails_bba64e5eb9; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@ -5842,6 +6010,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20140605125131'),
('20140605142133'),
('20140605151442'),
('20140606133116'),
('20140609092700'),
('20140609092827'),
('20140610153123'),
@ -5910,12 +6079,14 @@ INSERT INTO "schema_migrations" (version) VALUES
('20150507075620'),
('20150512123546'),
('20150520132030'),
('20150520133409'),
('20150526130729'),
('20150527153312'),
('20150529113555'),
('20150601125944'),
('20150603104502'),
('20150603104658'),
('20150603133050'),
('20150604081757'),
('20150604131525'),
('20150608142234'),
@ -5997,6 +6168,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20160905142700'),
('20160906094739'),
('20160906094847'),
('20160906145713'),
('20160915105234'),
('20161123104604'),
('20170109085345'),
@ -6071,6 +6243,8 @@ INSERT INTO "schema_migrations" (version) VALUES
('20210521085710'),
('20210525134018'),
('20210525150942'),
('20210608082748');
('20210608082748'),
('20210621122103'),
('20210621123954');