Merge pull request #7973 from apricot12/HQ_address_in_business_details

This commit is contained in:
Nihal M. Kelanthodika
2021-10-07 18:32:24 +05:30
committed by GitHub
30 changed files with 388 additions and 25 deletions

View File

@@ -1,5 +1,6 @@
# Used in enterprise new and edit forms to reset the state when the country is changed
angular.module("admin.enterprises").controller 'countryCtrl', ($scope, availableCountries) ->
angular.module("admin.enterprises").controller 'countryCtrl', ($scope, $timeout, availableCountries) ->
$scope.address_type = "address"
$scope.countries = availableCountries
$scope.countriesById = $scope.countries.reduce (obj, country) ->
@@ -7,12 +8,13 @@ angular.module("admin.enterprises").controller 'countryCtrl', ($scope, available
obj
, {}
$scope.$watch 'Enterprise.address.country_id', (newID, oldID) ->
$scope.clearState() unless $scope.addressStateMatchesCountry()
$timeout ->
$scope.$watch 'Enterprise.' + $scope.address_type + '.country_id', (newID, oldID) ->
$scope.clearState() unless $scope.addressStateMatchesCountry()
$scope.clearState = ->
$scope.Enterprise.address.state_id = null
$scope.Enterprise[$scope.address_type].state_id = null
$scope.addressStateMatchesCountry = ->
$scope.countriesById[$scope.Enterprise.address.country_id].states.some (state) ->
state.id == $scope.Enterprise.address.state_id
$scope.countriesById[$scope.Enterprise[$scope.address_type].country_id].states.some (state) ->
state.id == $scope.Enterprise[$scope.address_type].state_id

View File

@@ -0,0 +1,7 @@
%div
.margin-bottom-30
%p
{{ 'js.admin.modals.business_address_info.message' | t }}
.text-center
%input.button.red.icon-plus{ type: 'button', value: '{{ "js.admin.modals.got_it" | t }}', ng: { click: 'close()' } }

View File

@@ -10,6 +10,12 @@ input[type='button'], input[type='submit'] {
background-color: $disabled-background;
color: #ffffff;
}
&.secondary:disabled {
background-color: #ebf3fb;
border: 1px solid #ebf3fb;
color: #AFCFEF;
}
}
.select2-container-disabled {

View File

@@ -85,6 +85,20 @@ button, .button {
width: 100%;
text-align: center;
}
&.secondary {
background-color: transparent;
border: 1px solid $color-btn-bg;
color: $color-btn-bg;
&:hover, &:active, &:focus {
background-color: #ebf3fb;
}
&:active:focus {
box-shadow: none;
}
}
}
span.info {

View File

@@ -0,0 +1,14 @@
require 'active_support/concern'
module SetUnusedAddressFields
extend ActiveSupport::Concern
included do
self.before_validation :set_unused_address_fields
end
def set_unused_address_fields
ship_address.company = 'unused' if ship_address.present?
bill_address.company = 'unused' if bill_address.present?
end
end

View File

@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Customer < ApplicationRecord
include SetUnusedAddressFields
acts_as_taggable
searchable_attributes :name, :email, :code

View File

@@ -5,7 +5,6 @@ require 'spree/core/s3_support'
class Enterprise < ApplicationRecord
SELLS = %w(unspecified none own any).freeze
ENTERPRISE_SEARCH_RADIUS = 100
searchable_attributes :sells, :is_primary_producer
searchable_associations :properties
searchable_scopes :is_primary_producer, :is_distributor, :is_hub, :activated, :visible,
@@ -41,6 +40,7 @@ class Enterprise < ApplicationRecord
dependent: :destroy
has_many :distributed_orders, class_name: 'Spree::Order', foreign_key: 'distributor_id'
belongs_to :address, class_name: 'Spree::Address'
belongs_to :business_address, class_name: 'Spree::Address', dependent: :destroy
has_many :enterprise_fees
has_many :enterprise_roles, dependent: :destroy
has_many :users, through: :enterprise_roles
@@ -59,6 +59,7 @@ class Enterprise < ApplicationRecord
delegate :latitude, :longitude, :city, :state_name, to: :address
accepts_nested_attributes_for :address
accepts_nested_attributes_for :business_address, reject_if: :business_address_empty?, allow_destroy: true
accepts_nested_attributes_for :producer_properties, allow_destroy: true,
reject_if: lambda { |pp|
pp[:property_name].blank?
@@ -210,6 +211,13 @@ class Enterprise < ApplicationRecord
", one, one, others)
}
def business_address_empty?(attributes)
attributes_exists = attributes['id'].present?
attributes_empty = attributes.slice(:company, :address1, :city, :phone, :zipcode).values.all?(&:blank?)
attributes.merge!(_destroy: 1) if attributes_exists and attributes_empty
!attributes_exists && attributes_empty
end
# Force a distinct count to work around relation count issue https://github.com/rails/rails/issues/5554
def self.distinct_count
count(distinct: true)
@@ -414,7 +422,8 @@ class Enterprise < ApplicationRecord
end
def set_unused_address_fields
address.firstname = address.lastname = address.phone = 'unused' if address.present?
address.firstname = address.lastname = address.phone = address.company = 'unused' if address.present?
business_address.first_name = business_address.last_name = 'unused' if business_address.present?
end
def ensure_owner_is_manager

View File

@@ -54,7 +54,7 @@ class EnterpriseGroup < ApplicationRecord
}
def set_unused_address_fields
address.firstname = address.lastname = I18n.t(:unused)
address.firstname = address.lastname = address.company = I18n.t(:unused)
end
def set_undefined_address_fields

View File

@@ -13,7 +13,9 @@ module Spree
has_one :enterprise, dependent: :restrict_with_exception
has_many :shipments
validates :firstname, :lastname, :address1, :city, :country, presence: true
validates :address1, :city, :country, :phone, presence: true
validates :company, presence: true, unless: -> { first_name.blank? || last_name.blank?}
validates :firstname, :lastname, presence: true, unless: -> { company.present? }
validates :zipcode, presence: true, if: :require_zipcode?
validate :state_validate

View File

@@ -10,6 +10,7 @@ module Spree
include OrderShipment
include Checkout
include Balance
include SetUnusedAddressFields
searchable_attributes :number, :state, :shipment_state, :payment_state, :distributor_id,
:order_cycle_id, :email, :total

View File

@@ -2,6 +2,8 @@
module Spree
class User < ApplicationRecord
include SetUnusedAddressFields
searchable_attributes :email
devise :database_authenticatable, :token_authenticatable, :registerable, :recoverable,

View File

@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Subscription < ApplicationRecord
include SetUnusedAddressFields
ALLOWED_PAYMENT_METHOD_TYPES = ["Spree::PaymentMethod::Check",
"Spree::Gateway::StripeConnect",
"Spree::Gateway::StripeSCA"].freeze

View File

@@ -17,6 +17,7 @@ module Api
has_one :owner, serializer: Api::Admin::UserSerializer
has_many :users, serializer: Api::Admin::UserSerializer
has_one :address, serializer: Api::AddressSerializer
has_one :business_address, serializer: Api::AddressSerializer
def logo
attachment_urls(object.logo, [:thumb, :small, :medium])

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
module PermittedAttributes
class BusinessAddress
def self.attributes
[
:company, :address1, :address2,
:city, :country_id, :state_id, :zipcode,
:phone, :_destroy, :id
]
end
end
end

View File

@@ -17,6 +17,7 @@ module PermittedAttributes
group_ids: [], user_ids: [],
shipping_method_ids: [], payment_method_ids: [],
address_attributes: PermittedAttributes::Address.attributes,
business_address_attributes: PermittedAttributes::BusinessAddress.attributes,
producer_properties_attributes: [:id, :property_name, :value, :_destroy]
]
end

View File

@@ -0,0 +1,46 @@
.row
.three.columns.alpha
= bf.label :company, t(".company_legal_name")
%i.text-big.icon-question-sign.help-modal{ template: 'admin/modals/business_address_info.html' }
.eight.columns.omega
= bf.text_field :company, { placeholder: t(".company_placeholder") }
.row
.three.columns.alpha
= bf.label :address1, t('.address1')
%i.text-big.icon-question-sign.help-modal{ template: 'admin/modals/business_address_info.html' }
.eight.columns.omega
= bf.text_field :address1, { placeholder: t(".address1_placeholder") }
.row
.alpha.three.columns
= bf.label :address2, t(".address2")
.eight.columns.omega
= bf.text_field :address2
.row
.three.columns.alpha
= bf.label :city, t(:city)
\/
= bf.label :zipcode, t(:postcode)
.four.columns
= bf.text_field :city, { placeholder: t(:city_placeholder) }
.four.columns.omega
= bf.text_field :zipcode, { placeholder: t(:postcode_placeholder) }
.row
.three.columns.alpha
= bf.label :country_id, t(:country)
\/
= bf.label :state_id, t(:state)
%div{ "ng-controller": "countryCtrl", "ng-init": "address_type='business_address'" }
.four.columns
%input.ofn-select2.fullwidth#enterprise_business_address_attributes_country_id{ name: 'enterprise[business_address_attributes][country_id]', type: 'number', data: 'countries', placeholder: t('admin.choose'), ng: { model: 'Enterprise.business_address.country_id' } }
.four.columns.omega
%input.ofn-select2.fullwidth#enterprise_business_address_attributes_state_id{ name: 'enterprise[business_address_attributes][state_id]', type: 'number', data: 'countriesById[Enterprise.address.country_id].states', placeholder: t('admin.choose'), ng: { model: 'Enterprise.business_address.state_id' } }
.row
.three.columns.alpha
= bf.label :phone, t(".legal_phone_number")
%i.text-big.icon-question-sign.help-modal{ template: 'admin/modals/business_address_info.html' }
.eight.columns.omega
= bf.text_field :phone, { placeholder: t(".phone_placeholder") }

View File

@@ -47,3 +47,13 @@
.pad-top
%a.button.red{ href: '', ng: {click: 'removeTermsAndConditions()', if: 'Enterprise.terms_and_conditions'} }
= t('.remove_terms_and_conditions')
= f.fields_for :business_address, @enterprise.business_address || @enterprise.build_business_address do |bf|
%fieldset.alpha.no-border-bottom
%legend= t('business_address')
= render 'admin/enterprises/form/business_address', bf: bf
.row{"data-controller": "updateinput"}
= bf.hidden_field :_destroy, {"data-updateinput-target": "input"}
= f.submit t(".reset_form"), {class: 'secondary', "data-action": "click->updateinput#update", "data-updateinput-value": "true"}

View File

@@ -18,12 +18,18 @@
%h4= @order.order_cycle&.name
%tr{ valign: "top" }
%td{ align: "left", colspan: 3 }
%strong= "#{t('.from')}: #{@order.distributor.name}"
- if @order.distributor.business_address.blank?
%strong= "#{t('.from')}: #{@order.distributor.name}"
- else
%strong= "#{t('.from')}: #{@order.distributor.business_address.company}"
- if @order.distributor.abn.present?
%br
= "#{t(:abn)} #{@order.distributor.abn}"
%br
= @order.distributor.address.full_address
- if @order.distributor.business_address.blank?
= @order.distributor.address.full_address
- else
= @order.distributor.business_address.full_address
%br
= @order.distributor.contact.email
%tr{ valign: "top" }

View File

@@ -12,16 +12,28 @@
= wicked_pdf_image_tag @order.distributor.logo(:small), width: 150, height: 150
%tr{ valign: "top" }
%td{ :align => "left" }
%strong= @order.distributor.name
%br
= @order.distributor.address.address_part1
%br
= @order.distributor.address.address_part2
%br
= @order.distributor.email_address
- if @order.distributor.phone.present?
- if @order.distributor.business_address.blank?
%strong= @order.distributor.name
%br
= @order.distributor.phone
= @order.distributor.address.address_part1
%br
= @order.distributor.address.address_part2
%br
= @order.distributor.email_address
- if @order.distributor.phone.present?
%br
= @order.distributor.phone
- else
%strong= @order.distributor.business_address.company
%br
= @order.distributor.business_address.address_part1
%br
= @order.distributor.business_address.address_part2
%br
= @order.distributor.email_address
- if @order.distributor.business_address.phone.present?
%br
= @order.distributor.business_address.phone
- if @order.distributor.abn.present?
%br
= "#{t :abn} #{@order.distributor.abn}"

View File

@@ -18,6 +18,8 @@
= render "spree/admin/shared/translations"
= render "spree/admin/shared/routes"
= javascript_pack_tag "admin", "data-turbo-track": "reload"
%script
= raw "var AUTH_TOKEN = \"#{form_authenticity_token}\";"

View File

@@ -0,0 +1,13 @@
import { Controller } from "stimulus";
export default class extends Controller {
static targets = ["input"];
update(event) {
const value = event.currentTarget.dataset.updateinputValue;
this.inputTargets.forEach((t) => {
t.value = value;
});
}
}

View File

@@ -0,0 +1,6 @@
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"
const application = Application.start()
const context = require.context("controllers", true, /.js$/)
application.load(definitionsFromContext(context))

View File

@@ -265,6 +265,7 @@ en:
producers: 'Australian Producers'
producers_join: Australian producers are now welcome to join the Open Food Network. #FIXME
charges_sales_tax: Charges GST?
business_address: "Business Address"
print_invoice: "Print Invoice"
print_ticket: "Print Ticket"
select_ticket_printer: "Select printer for tickets"
@@ -774,6 +775,15 @@ en:
terms_and_conditions: "Terms and Conditions"
remove_terms_and_conditions: "Remove File"
uploaded_on: "uploaded on"
reset_form: "Reset Form"
business_address:
company_legal_name: Company Legal Name
company_placeholder: Example Inc.
address1: Legal address
address1_placeholder: 123 High St.
address2: Address(contd.)
legal_phone_number: Legal phone number
phone_placeholder: "98 123 4565"
contact:
name: Name
name_placeholder: eg. Gustav Plum
@@ -2739,6 +2749,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using
title: "Uploading Terms and Conditions"
message_1: "All your buyers will have to agree to them once at checkout. If you update the file, all your buyers will have to agree to them again at checkout."
message_2: "For buyers with subscriptions, you need to email them the Terms and Conditions (or the changes to them) for now, nothing will notify them about these new Terms and Conditions."
business_address_info:
message: "Company Legal Name, Legal Address and Legal Phone number are used for businesses that invoice from a legal entity registered with different details to their public trading information. These details will ONLY be used on invoices. If these details are blank your public Name, Address and Phone Number will be used on invoices."
panels:
save: SAVE
saved: SAVED

View File

@@ -0,0 +1,5 @@
class AddBusinessAddressIdToEnterprises < ActiveRecord::Migration[6.1]
def change
add_column :enterprises, :business_address_id, :integer
end
end

View File

@@ -209,6 +209,7 @@ ActiveRecord::Schema.define(version: 2021_09_27_091723) do
t.string "terms_and_conditions_content_type", limit: 255
t.integer "terms_and_conditions_file_size"
t.datetime "terms_and_conditions_updated_at"
t.integer "business_address_id"
t.index ["address_id"], name: "index_enterprises_on_address_id"
t.index ["is_primary_producer", "sells"], name: "index_enterprises_on_is_primary_producer_and_sells"
t.index ["name"], name: "index_enterprises_on_name", unique: true

View File

@@ -4,7 +4,7 @@ FactoryBot.define do
factory :address, aliases: [:bill_address, :ship_address], class: Spree::Address do
firstname { 'John' }
lastname { 'Doe' }
company { 'Company' }
company { 'unused' }
address1 { '10 Lovely Street' }
address2 { 'Northwest' }
city { 'Herndon' }

View File

@@ -0,0 +1,143 @@
require "spec_helper"
describe "Business Address" do
include WebHelper
include AuthenticationHelper
context "as an Enterprise user", js: true do
let(:enterprise_user) { create(:user, enterprise_limit: 1) }
let(:distributor) { create(:distributor_enterprise, name: "First Distributor") }
before do
enterprise_user.enterprise_roles.build(enterprise: distributor).save!
login_as enterprise_user
visit edit_admin_enterprise_path(distributor)
end
describe "Business Address form" do
def go_to_business_details
within(".side_menu") do
click_link "Business Details"
end
end
before do
go_to_business_details
end
it 'adds a business address' do
fill_in 'enterprise_business_address_attributes_company', with: 'Company'
fill_in 'enterprise_business_address_attributes_address1', with: '35 Ballantyne St'
fill_in 'enterprise_business_address_attributes_city', with: 'Thornbury'
fill_in 'enterprise_business_address_attributes_zipcode', with: '3072'
select2_select 'Australia', from: 'enterprise_business_address_attributes_country_id'
select2_select 'Victoria', from: 'enterprise_business_address_attributes_state_id'
fill_in 'enterprise_business_address_attributes_phone', with: '0123456789'
click_button "Update"
expect(page).to have_content("Enterprise \"First Distributor\" has been successfully updated!")
end
it 'is missing company field' do
fill_in 'enterprise_business_address_attributes_address1', with: '35 Ballantyne St'
fill_in 'enterprise_business_address_attributes_city', with: 'Thornbury'
fill_in 'enterprise_business_address_attributes_zipcode', with: '3072'
select2_select 'Australia', from: 'enterprise_business_address_attributes_country_id'
select2_select 'Victoria', from: 'enterprise_business_address_attributes_state_id'
fill_in 'enterprise_business_address_attributes_phone', with: '0123456789'
click_button "Update"
expect(page).to have_content("Business address company can't be blank")
end
it 'is missing address field' do
fill_in 'enterprise_business_address_attributes_company', with: 'Company'
fill_in 'enterprise_business_address_attributes_city', with: 'Thornbury'
fill_in 'enterprise_business_address_attributes_zipcode', with: '3072'
select2_select 'Australia', from: 'enterprise_business_address_attributes_country_id'
select2_select 'Victoria', from: 'enterprise_business_address_attributes_state_id'
fill_in 'enterprise_business_address_attributes_phone', with: '0123456789'
click_button "Update"
expect(page).to have_content("Business address address1 can't be blank")
end
it 'is missing city field' do
fill_in 'enterprise_business_address_attributes_company', with: 'Company'
fill_in 'enterprise_business_address_attributes_address1', with: '35 Ballantyne St'
fill_in 'enterprise_business_address_attributes_zipcode', with: '3072'
select2_select 'Australia', from: 'enterprise_business_address_attributes_country_id'
select2_select 'Victoria', from: 'enterprise_business_address_attributes_state_id'
fill_in 'enterprise_business_address_attributes_phone', with: '0123456789'
click_button "Update"
expect(page).to have_content("Business address city can't be blank")
end
it 'is missing zipcode field' do
fill_in 'enterprise_business_address_attributes_company', with: 'Company'
fill_in 'enterprise_business_address_attributes_address1', with: '35 Ballantyne St'
fill_in 'enterprise_business_address_attributes_city', with: 'Thornbury'
select2_select 'Australia', from: 'enterprise_business_address_attributes_country_id'
select2_select 'Victoria', from: 'enterprise_business_address_attributes_state_id'
fill_in 'enterprise_business_address_attributes_phone', with: '0123456789'
click_button "Update"
expect(page).to have_content("Business address zipcode can't be blank")
end
it 'is missing phone field' do
fill_in 'enterprise_business_address_attributes_company', with: 'Company'
fill_in 'enterprise_business_address_attributes_address1', with: '35 Ballantyne St'
fill_in 'enterprise_business_address_attributes_city', with: 'Thornbury'
fill_in 'enterprise_business_address_attributes_zipcode', with: '3072'
select2_select 'Australia', from: 'enterprise_business_address_attributes_country_id'
select2_select 'Victoria', from: 'enterprise_business_address_attributes_state_id'
click_button "Update"
expect(page).to have_content("Business address phone can't be blank")
end
it 'destroys business address when Reset Form button is clicked' do
fill_in 'enterprise_business_address_attributes_company', with: 'Company'
fill_in 'enterprise_business_address_attributes_address1', with: '35 Ballantyne St'
fill_in 'enterprise_business_address_attributes_city', with: 'Thornbury'
fill_in 'enterprise_business_address_attributes_zipcode', with: '3072'
select2_select 'Australia', from: 'enterprise_business_address_attributes_country_id'
select2_select 'Victoria', from: 'enterprise_business_address_attributes_state_id'
fill_in 'enterprise_business_address_attributes_phone', with: '0123456789'
click_button "Update"
go_to_business_details
click_button "Reset Form"
expect(page).to have_content("Enterprise \"First Distributor\" has been successfully updated!")
end
it 'clears form when all fields are empty' do
fill_in 'enterprise_business_address_attributes_company', with: 'Company'
fill_in 'enterprise_business_address_attributes_address1', with: '35 Ballantyne St'
fill_in 'enterprise_business_address_attributes_city', with: 'Thornbury'
fill_in 'enterprise_business_address_attributes_zipcode', with: '3072'
select2_select 'Australia', from: 'enterprise_business_address_attributes_country_id'
select2_select 'Victoria', from: 'enterprise_business_address_attributes_state_id'
fill_in 'enterprise_business_address_attributes_phone', with: '0123456789'
click_button "Update"
go_to_business_details
fill_in 'enterprise_business_address_attributes_company', with: ''
fill_in 'enterprise_business_address_attributes_address1', with: ''
fill_in 'enterprise_business_address_attributes_city', with: ''
fill_in 'enterprise_business_address_attributes_zipcode', with: ''
fill_in 'enterprise_business_address_attributes_phone', with: ''
click_button "Update"
expect(page).to have_content("Enterprise \"First Distributor\" has been successfully updated!")
end
end
end
end

View File

@@ -0,0 +1,30 @@
/**
* @jest-environment jsdom
*/
import { Application } from "stimulus";
import updateinput_controller from "../../../app/webpacker/controllers/updateinput_controller";
describe("updateInput controller", () => {
describe("#update", () => {
beforeEach(() => {
document.body.innerHTML = `<form data-controller="updateinput">
<input id="input" type="hidden" value="false" data-updateinput-target="input" />
<div id="submit" data-action="click->updateinput#update" data-updateinput-value="true" />
</form>`;
const application = Application.start();
application.register("updateinput", updateinput_controller);
});
it("update the input value", () => {
const submit = document.getElementById("submit");
const input = document.getElementById("input");
expect(input.value).toBe("false");
submit.click();
expect(input.value).toBe("true");
});
});
});

View File

@@ -23,6 +23,7 @@ describe Enterprise do
it { is_expected.to have_many(:supplied_products) }
it { is_expected.to have_many(:distributed_orders) }
it { is_expected.to belong_to(:address) }
it { is_expected.to belong_to(:business_address) }
it "destroys enterprise roles upon its own demise" do
e = create(:enterprise)

View File

@@ -14,7 +14,7 @@ describe Spree::Address do
country: state.country,
firstname: 'firstname',
lastname: 'lastname',
company: 'company',
company: 'unused',
phone: 'phone',
state_id: state.id,
state_name: state.name,
@@ -110,10 +110,10 @@ describe Spree::Address do
expect(address).to be_valid
end
it "does not require phone" do
it "requires phone" do
address.phone = ""
address.valid?
expect(address.errors[:phone]).to be_empty
expect(address.errors[:phone].first).to eq "can't be blank"
end
it "requires zipcode" do