mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
better error handling in stripe::subscription creation process
This commit is contained in:
parent
32b0222da5
commit
f661428db2
@ -7,7 +7,7 @@ class API::PaymentSchedulesController < API::ApiController
|
||||
|
||||
def download
|
||||
authorize @payment_schedule
|
||||
# TODO, send_file File.join(Rails.root, @payment_schedule.file), type: 'application/pdf', disposition: 'attachment'
|
||||
send_file File.join(Rails.root, @payment_schedule.file), type: 'application/pdf', disposition: 'attachment'
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -89,8 +89,8 @@ class API::PaymentsController < API::ApiController
|
||||
end
|
||||
|
||||
render generate_payment_response(intent, res)
|
||||
rescue Stripe::InvalidRequestError
|
||||
render json: { error: 'no such setup intent' }, status: :unprocessable_entity
|
||||
rescue Stripe::InvalidRequestError => e
|
||||
render json: e, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -19,15 +19,16 @@ client.interceptors.response.use(function (response) {
|
||||
});
|
||||
|
||||
function extractHumanReadableMessage(error: any): string {
|
||||
if (error.match(/^<!DOCTYPE html>/)) {
|
||||
// parse ruby error pages
|
||||
const parser = new DOMParser();
|
||||
const htmlDoc = parser.parseFromString(error, 'text/html');
|
||||
return htmlDoc.querySelector('h2').textContent;
|
||||
if (typeof error === 'string') {
|
||||
if (error.match(/^<!DOCTYPE html>/)) {
|
||||
// parse ruby error pages
|
||||
const parser = new DOMParser();
|
||||
const htmlDoc = parser.parseFromString(error, 'text/html');
|
||||
return htmlDoc.querySelector('h2').textContent;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
if (typeof error === 'string') return error;
|
||||
|
||||
// parse Rails errors (as JSON)
|
||||
let message = '';
|
||||
if (error instanceof Object) {
|
||||
|
@ -11,6 +11,7 @@ interface StripeFormProps {
|
||||
onSuccess: (result: SetupIntent|PaymentConfirmation|any) => void,
|
||||
onError: (message: string) => void,
|
||||
customer: User,
|
||||
operator: User,
|
||||
className?: string,
|
||||
paymentSchedule?: boolean,
|
||||
cartItems?: CartItems
|
||||
@ -20,7 +21,7 @@ interface StripeFormProps {
|
||||
* A form component to collect the credit card details and to create the payment method on Stripe.
|
||||
* The form validation button must be created elsewhere, using the attribute form="stripe-form".
|
||||
*/
|
||||
export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onError, children, className, paymentSchedule = false, cartItems, customer }) => {
|
||||
export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onError, children, className, paymentSchedule = false, cartItems, customer, operator }) => {
|
||||
|
||||
const { t } = useTranslation('shared');
|
||||
|
||||
@ -57,7 +58,16 @@ export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onE
|
||||
// we start by associating the payment method with the user
|
||||
const { client_secret } = await PaymentAPI.setupIntent(customer.id);
|
||||
const { setupIntent, error } = await stripe.confirmCardSetup(client_secret, {
|
||||
payment_method: paymentMethod.id
|
||||
payment_method: paymentMethod.id,
|
||||
mandate_data: {
|
||||
customer_acceptance: {
|
||||
type: 'online',
|
||||
online: {
|
||||
ip_address: operator.ip_address,
|
||||
user_agent: navigator.userAgent
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if (error) {
|
||||
onError(error.message);
|
||||
|
@ -178,6 +178,7 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
|
||||
<StripeForm onSubmit={handleSubmit}
|
||||
onSuccess={handleFormSuccess}
|
||||
onError={handleFormError}
|
||||
operator={currentUser}
|
||||
className="stripe-form"
|
||||
cartItems={cartItems}
|
||||
customer={customer}
|
||||
|
@ -15,6 +15,7 @@ export interface User {
|
||||
role: UserRole
|
||||
name: string,
|
||||
need_completion: boolean,
|
||||
ip_address: string,
|
||||
profile: {
|
||||
id: number,
|
||||
first_name: string,
|
||||
|
@ -63,7 +63,7 @@ class PaymentScheduleService
|
||||
item.save!
|
||||
end
|
||||
|
||||
StripeWorker.perform_async(:create_stripe_subscription, ps.id, reservation&.reservable&.stp_product_id) if payment_method == 'stripe'
|
||||
StripeService.create_stripe_subscription(ps.id, reservation&.reservable&.stp_product_id) if payment_method == 'stripe'
|
||||
ps
|
||||
end
|
||||
end
|
||||
|
@ -17,20 +17,22 @@ class Reservations::Reserve
|
||||
user = User.find(user_id)
|
||||
reservation.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id
|
||||
|
||||
reservation.pre_check
|
||||
payment = if schedule
|
||||
generate_schedule(reservation: reservation,
|
||||
total: payment_details[:before_coupon],
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: user,
|
||||
payment_method: payment_method,
|
||||
coupon_code: payment_details[:coupon])
|
||||
else
|
||||
generate_invoice(reservation, operator_profile_id, payment_details, payment_intent_id)
|
||||
end
|
||||
payment.save
|
||||
WalletService.debit_user_wallet(payment, user, reservation)
|
||||
reservation.post_save
|
||||
ActiveRecord::Base.transaction do
|
||||
reservation.pre_check
|
||||
payment = if schedule
|
||||
generate_schedule(reservation: reservation,
|
||||
total: payment_details[:before_coupon],
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: user,
|
||||
payment_method: payment_method,
|
||||
coupon_code: payment_details[:coupon])
|
||||
else
|
||||
generate_invoice(reservation, operator_profile_id, payment_details, payment_intent_id)
|
||||
end
|
||||
payment.save
|
||||
WalletService.debit_user_wallet(payment, user, reservation)
|
||||
reservation.post_save
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
|
71
app/services/stripe_service.rb
Normal file
71
app/services/stripe_service.rb
Normal file
@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Helpers and utilities for interactions with the Stripe payment gateway
|
||||
class StripeService
|
||||
class << self
|
||||
|
||||
# Create the provided PaymentSchedule on Stripe, using the Subscription API
|
||||
def create_stripe_subscription(payment_schedule_id, reservable_stp_id)
|
||||
payment_schedule = PaymentSchedule.find(payment_schedule_id)
|
||||
first_item = payment_schedule.ordered_items.first
|
||||
|
||||
# subscription (recurring price)
|
||||
price = create_price(first_item.details['recurring'],
|
||||
payment_schedule.scheduled.plan.stp_product_id,
|
||||
nil, monthly: true)
|
||||
# other items (not recurring)
|
||||
items = subscription_invoice_items(payment_schedule, first_item, reservable_stp_id)
|
||||
|
||||
stp_subscription = Stripe::Subscription.create({
|
||||
customer: payment_schedule.invoicing_profile.user.stp_customer_id,
|
||||
cancel_at: payment_schedule.scheduled.expiration_date.to_i,
|
||||
promotion_code: payment_schedule.coupon&.code,
|
||||
add_invoice_items: items,
|
||||
items: [
|
||||
{ price: price[:id] }
|
||||
]
|
||||
}, { api_key: Setting.get('stripe_secret_key') })
|
||||
payment_schedule.update_attributes(stp_subscription_id: stp_subscription.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subscription_invoice_items(payment_schedule, first_item, reservable_stp_id)
|
||||
second_item = payment_schedule.ordered_items[1]
|
||||
|
||||
items = []
|
||||
if first_item.amount != second_item.amount
|
||||
unless first_item.details['adjustment']&.zero?
|
||||
# adjustment: when dividing the price of the plan / months, sometimes it forces us to round the amount per month.
|
||||
# The difference is invoiced here
|
||||
p1 = create_price(first_item.details['adjustment'],
|
||||
payment_schedule.scheduled.plan.stp_product_id,
|
||||
"Price adjustment for payment schedule #{payment_schedule.id}")
|
||||
items.push(price: p1[:id])
|
||||
end
|
||||
unless first_item.details['other_items']&.zero?
|
||||
# when taking a subscription at the same time of a reservation (space, machine or training), the amount of the
|
||||
# reservation is invoiced here.
|
||||
p2 = create_price(first_item.details['other_items'],
|
||||
reservable_stp_id,
|
||||
"Reservations for payment schedule #{payment_schedule.id}")
|
||||
items.push(price: p2[:id])
|
||||
end
|
||||
end
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
def create_price(amount, stp_product_id, name, monthly: false)
|
||||
params = {
|
||||
unit_amount: amount,
|
||||
currency: Setting.get('stripe_currency'),
|
||||
product: stp_product_id,
|
||||
nickname: name
|
||||
}
|
||||
params[:recurring] = { interval: 'month', interval_count: 1 } if monthly
|
||||
|
||||
Stripe::Price.create(params, { api_key: Setting.get('stripe_secret_key') })
|
||||
end
|
||||
end
|
||||
end
|
@ -19,22 +19,24 @@ class Subscriptions::Subscribe
|
||||
def pay_and_save(subscription, payment_details: nil, payment_intent_id: nil, schedule: false, payment_method: nil)
|
||||
return false if user_id.nil?
|
||||
|
||||
subscription.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id
|
||||
subscription.init_save
|
||||
user = User.find(user_id)
|
||||
subscription.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id
|
||||
|
||||
payment = if schedule
|
||||
generate_schedule(subscription: subscription,
|
||||
total: payment_details[:before_coupon],
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: user,
|
||||
payment_method: payment_method,
|
||||
coupon_code: payment_details[:coupon])
|
||||
else
|
||||
generate_invoice(subscription, operator_profile_id, payment_details, payment_intent_id)
|
||||
end
|
||||
payment.save
|
||||
WalletService.debit_user_wallet(payment, user, subscription)
|
||||
ActiveRecord::Base.transaction do
|
||||
subscription.init_save
|
||||
payment = if schedule
|
||||
generate_schedule(subscription: subscription,
|
||||
total: payment_details[:before_coupon],
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: user,
|
||||
payment_method: payment_method,
|
||||
coupon_code: payment_details[:coupon])
|
||||
else
|
||||
generate_invoice(subscription, operator_profile_id, payment_details, payment_intent_id)
|
||||
end
|
||||
payment.save
|
||||
WalletService.debit_user_wallet(payment, user, subscription)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
|
@ -4,6 +4,7 @@ json.extract! member, :id, :username, :email, :group_id
|
||||
json.role member.roles.first.name
|
||||
json.name member.profile.full_name
|
||||
json.need_completion member.need_completion?
|
||||
json.ip_address member.current_sign_in_ip.to_s
|
||||
|
||||
json.profile do
|
||||
json.id member.profile.id
|
||||
|
21
app/workers/payment_schedule_worker.rb
Normal file
21
app/workers/payment_schedule_worker.rb
Normal file
@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Periodically checks if a PaymentScheduleItem cames to its due date.
|
||||
# If this is the case
|
||||
class PaymentScheduleWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform
|
||||
PaymentScheduleItem.where(due_date: [DateTime.current.at_beginning_of_day, DateTime.current.end_of_day], state: 'new').each do |psi|
|
||||
# the following depends on the payment method (stripe/check)
|
||||
# if stripe:
|
||||
# - verify the payment was successful
|
||||
# - if not, alert the admins
|
||||
# - if succeeded, generate the invoice
|
||||
# if check:
|
||||
# - alert the admins and the user that it is time to bank the check
|
||||
# - generate the invoice
|
||||
# finally, in any cases, update the psi.state field according to the new status
|
||||
end
|
||||
end
|
||||
end
|
@ -66,57 +66,4 @@ class StripeWorker
|
||||
object.update_attributes(stp_product_id: product.id)
|
||||
end
|
||||
end
|
||||
|
||||
def create_stripe_subscription(payment_schedule_id, reservable_stp_id)
|
||||
payment_schedule = PaymentSchedule.find(payment_schedule_id)
|
||||
|
||||
first_item = payment_schedule.ordered_items.first
|
||||
second_item = payment_schedule.ordered_items[1]
|
||||
|
||||
items = []
|
||||
if first_item.amount != second_item.amount
|
||||
unless first_item.details['adjustment']&.zero?
|
||||
# adjustment: when dividing the price of the plan / months, sometimes it forces us to round the amount per month.
|
||||
# The difference is invoiced here
|
||||
p1 = Stripe::Price.create({
|
||||
unit_amount: first_item.details['adjustment'],
|
||||
currency: Setting.get('stripe_currency'),
|
||||
product: payment_schedule.scheduled.plan.stp_product_id,
|
||||
nickname: "Price adjustment for payment schedule #{payment_schedule_id}"
|
||||
}, { api_key: Setting.get('stripe_secret_key') })
|
||||
items.push(price: p1[:id])
|
||||
end
|
||||
unless first_item.details['other_items']&.zero?
|
||||
# when taking a subscription at the same time of a reservation (space, machine or training), the amount of the
|
||||
# reservation is invoiced here.
|
||||
p2 = Stripe::Price.create({
|
||||
unit_amount: first_item.details['other_items'],
|
||||
currency: Setting.get('stripe_currency'),
|
||||
product: reservable_stp_id,
|
||||
nickname: "Reservations for payment schedule #{payment_schedule_id}"
|
||||
}, { api_key: Setting.get('stripe_secret_key') })
|
||||
items.push(price: p2[:id])
|
||||
end
|
||||
end
|
||||
|
||||
# subscription (recurring price)
|
||||
price = Stripe::Price.create({
|
||||
unit_amount: first_item.details['recurring'],
|
||||
currency: Setting.get('stripe_currency'),
|
||||
recurring: { interval: 'month', interval_count: 1 },
|
||||
product: payment_schedule.scheduled.plan.stp_product_id
|
||||
},
|
||||
{ api_key: Setting.get('stripe_secret_key') })
|
||||
|
||||
stp_subscription = Stripe::Subscription.create({
|
||||
customer: payment_schedule.invoicing_profile.user.stp_customer_id,
|
||||
cancel_at: payment_schedule.scheduled.expiration_date.to_i,
|
||||
promotion_code: payment_schedule.coupon&.code,
|
||||
add_invoice_items: items,
|
||||
items: [
|
||||
{ price: price[:id] }
|
||||
]
|
||||
}, { api_key: Setting.get('stripe_secret_key') })
|
||||
payment_schedule.update_attributes(stp_subscription_id: stp_subscription.id)
|
||||
end
|
||||
end
|
||||
|
@ -6,6 +6,7 @@ class CreatePaymentScheduleItems < ActiveRecord::Migration[5.2]
|
||||
create_table :payment_schedule_items do |t|
|
||||
t.integer :amount
|
||||
t.datetime :due_date
|
||||
t.string :state, default: 'new'
|
||||
t.jsonb :details, default: '{}'
|
||||
t.belongs_to :payment_schedule, foreign_key: true
|
||||
t.belongs_to :invoice, foreign_key: true
|
||||
|
@ -78,11 +78,11 @@ CREATE FUNCTION public.fill_search_vector_for_project() RETURNS trigger
|
||||
select string_agg(description, ' ') as content into step_description from project_steps where project_id = new.id;
|
||||
|
||||
new.search_vector :=
|
||||
setweight(to_tsvector('pg_catalog.french', unaccent(coalesce(new.name, ''))), 'A') ||
|
||||
setweight(to_tsvector('pg_catalog.french', unaccent(coalesce(new.tags, ''))), 'B') ||
|
||||
setweight(to_tsvector('pg_catalog.french', unaccent(coalesce(new.description, ''))), 'D') ||
|
||||
setweight(to_tsvector('pg_catalog.french', unaccent(coalesce(step_title.title, ''))), 'C') ||
|
||||
setweight(to_tsvector('pg_catalog.french', unaccent(coalesce(step_description.content, ''))), 'D');
|
||||
setweight(to_tsvector('pg_catalog.simple', unaccent(coalesce(new.name, ''))), 'A') ||
|
||||
setweight(to_tsvector('pg_catalog.simple', unaccent(coalesce(new.tags, ''))), 'B') ||
|
||||
setweight(to_tsvector('pg_catalog.simple', unaccent(coalesce(new.description, ''))), 'D') ||
|
||||
setweight(to_tsvector('pg_catalog.simple', unaccent(coalesce(step_title.title, ''))), 'C') ||
|
||||
setweight(to_tsvector('pg_catalog.simple', unaccent(coalesce(step_description.content, ''))), 'D');
|
||||
|
||||
return new;
|
||||
end
|
||||
@ -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,
|
||||
@ -1046,8 +1046,8 @@ ALTER SEQUENCE public.invoice_items_id_seq OWNED BY public.invoice_items.id;
|
||||
|
||||
CREATE TABLE public.invoices (
|
||||
id integer NOT NULL,
|
||||
invoiced_type character varying,
|
||||
invoiced_id integer,
|
||||
invoiced_type character varying,
|
||||
stp_invoice_id character varying,
|
||||
total integer,
|
||||
created_at timestamp without time zone,
|
||||
@ -1227,15 +1227,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
|
||||
);
|
||||
|
||||
|
||||
@ -1470,6 +1470,7 @@ CREATE TABLE public.payment_schedule_items (
|
||||
id bigint NOT NULL,
|
||||
amount integer,
|
||||
due_date timestamp without time zone,
|
||||
state character varying DEFAULT 'new'::character varying,
|
||||
details jsonb DEFAULT '"{}"'::jsonb,
|
||||
payment_schedule_id bigint,
|
||||
invoice_id bigint,
|
||||
@ -1657,8 +1658,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
|
||||
@ -1973,8 +1974,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
|
||||
);
|
||||
@ -2006,8 +2007,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
|
||||
);
|
||||
@ -2943,8 +2944,8 @@ CREATE TABLE public.users_roles (
|
||||
CREATE TABLE public.wallet_transactions (
|
||||
id integer NOT NULL,
|
||||
wallet_id integer,
|
||||
transactable_type character varying,
|
||||
transactable_id integer,
|
||||
transactable_type character varying,
|
||||
transaction_type character varying,
|
||||
amount integer,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
@ -4033,14 +4034,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: -
|
||||
--
|
||||
@ -5105,6 +5098,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: -
|
||||
--
|
||||
@ -5639,6 +5655,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20140605125131'),
|
||||
('20140605142133'),
|
||||
('20140605151442'),
|
||||
('20140606133116'),
|
||||
('20140609092700'),
|
||||
('20140609092827'),
|
||||
('20140610153123'),
|
||||
@ -5707,12 +5724,14 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20150507075620'),
|
||||
('20150512123546'),
|
||||
('20150520132030'),
|
||||
('20150520133409'),
|
||||
('20150526130729'),
|
||||
('20150527153312'),
|
||||
('20150529113555'),
|
||||
('20150601125944'),
|
||||
('20150603104502'),
|
||||
('20150603104658'),
|
||||
('20150603133050'),
|
||||
('20150604081757'),
|
||||
('20150604131525'),
|
||||
('20150608142234'),
|
||||
@ -5794,6 +5813,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20160905142700'),
|
||||
('20160906094739'),
|
||||
('20160906094847'),
|
||||
('20160906145713'),
|
||||
('20160915105234'),
|
||||
('20161123104604'),
|
||||
('20170109085345'),
|
||||
|
Loading…
x
Reference in New Issue
Block a user