Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add vat number #2764

Merged
merged 14 commits into from
Dec 26, 2023
1 change: 1 addition & 0 deletions ecommerce/mail_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ def send_ecommerce_order_receipt(order, cyber_source_provided_email=None):
"city": purchaser.get("city"),
"country": country.name if country else None,
"company": purchaser.get("company"),
"vat_id": purchaser.get("vat_id"),
},
"enable_taxes_display": settings.FEATURES.get(
"ENABLE_TAXES_DISPLAY", False
Expand Down
5 changes: 4 additions & 1 deletion ecommerce/mail_api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,9 @@ def test_send_b2b_receipt_email_error(mocker):
}
],
)
def test_send_ecommerce_order_receipt(mocker, receipt_data):
def test_send_ecommerce_order_receipt(mocker, receipt_data, settings):
"""send_ecommerce_order_receipt should send a receipt email"""
settings.FEATURES["ENABLE_TAXES_DISPLAY"] = False
patched_mail_api = mocker.patch("ecommerce.mail_api.api")
date = datetime.datetime(2010, 1, 1, 0, tzinfo=UTC)
user = UserFactory.create(
Expand All @@ -232,6 +233,7 @@ def test_send_ecommerce_order_receipt(mocker, receipt_data):
legal_address__state_or_territory="US-CO",
legal_address__city="Boulder",
legal_address__postal_code="80309",
legal_address__vat_id="AT12349876",
)
line = LineFactory.create(
order__status=Order.CREATED,
Expand Down Expand Up @@ -301,6 +303,7 @@ def test_send_ecommerce_order_receipt(mocker, receipt_data):
"city": "Boulder",
"country": "United States",
"company": user.profile.company,
"vat_id": "AT12349876",
},
"enable_taxes_display": False,
},
Expand Down
1 change: 1 addition & 0 deletions ecommerce/serializers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ def test_serialize_order_receipt(receipt_data):
"state_or_territory": purchaser.state_or_territory,
"city": purchaser.city,
"postal_code": purchaser.postal_code,
"vat_id": purchaser.vat_id,
"company": order.purchaser.profile.company,
"street_address": [
line
Expand Down
1 change: 1 addition & 0 deletions hubspot_xpro/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def make_contact_sync_message(user_id: int) -> SimplePublicObjectInput:
"job_function": "job_function",
"leadership_level": "leadership_level",
"highest_education": "highest_education",
"vat_id": "vat_id",
}

user = User.objects.get(id=user_id)
Expand Down
1 change: 1 addition & 0 deletions hubspot_xpro/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def test_make_contact_sync_message(user):
"name": user.name,
"state": user.legal_address.state_or_territory,
"zip": user.legal_address.postal_code,
"vat_id": user.legal_address.vat_id or "",
}


Expand Down
3 changes: 3 additions & 0 deletions mail/templates/product_order_receipt/body.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ <h3 style="color: #000000; font-size: 16px; font-weight: 700; line-height: 18px;
</table>
{% endif %}
<strong style="font-weight: 700;">Email Address:</strong> {{ purchaser.email }}<br>
{% if purchaser.vat_id %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we place it behind the tax display feat flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is related to the taxes feature as it was an extra thing required to avoid some manual work. @cachob What are your thoughts on this?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a separate issue from country taxes. A learner may be required to enter a VAT number (by the employer for example) but the country itself may not require a learner's tax

<strong style="font-weight: 700;">VAT ID:</strong> {{ purchaser.vat_id }}<br>
{% endif %}
</p>
{% if receipt or coupon %}
<h3 style="color: #000000; font-size: 16px; font-weight: 700; line-height: 18px; margin: 0 0 20px;">Payment Information</h3>
Expand Down
8 changes: 6 additions & 2 deletions static/js/components/forms/EditProfileForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import type { Country, User } from "../../flow/authTypes"
type Props = {
onSubmit: Function,
countries: Array<Country>,
user: User
user: User,
isVatEnabled: boolean,
enableVatID: Function,
}

const getInitialValues = (user: User) => ({
Expand All @@ -34,7 +36,7 @@ const getInitialValues = (user: User) => ({
}
})

const EditProfileForm = ({ onSubmit, countries, user }: Props) => (
const EditProfileForm = ({ onSubmit, countries, user, isVatEnabled, enableVatID }: Props) => (
<Formik
onSubmit={onSubmit}
validationSchema={legalAddressValidation.concat(profileValidation)}
Expand All @@ -47,6 +49,8 @@ const EditProfileForm = ({ onSubmit, countries, user }: Props) => (
setFieldTouched={setFieldTouched}
values={values}
includePassword={false}
isVatEnabled={isVatEnabled}
enableVatID={enableVatID}
/>
<ProfileFields />
<div className="row-inner justify-content-end">
Expand Down
18 changes: 16 additions & 2 deletions static/js/components/forms/EditProfileForm_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ import {
import { makeCountries, makeUser } from "../../factories/user"

describe("EditProfileForm", () => {
let sandbox, onSubmitStub
let sandbox, onSubmitStub, enableVatIDStub

const countries = makeCountries()
const user = makeUser()

const renderForm = () =>
const renderForm = (isVatEnabled = false) =>
mount(
<EditProfileForm
onSubmit={onSubmitStub}
countries={countries}
user={user}
isVatEnabled={isVatEnabled}
enableVatID={enableVatIDStub}
/>
)

Expand All @@ -46,9 +48,21 @@ describe("EditProfileForm", () => {
assert.ok(findFormikFieldByName(form, "profile.birth_year").exists())
assert.ok(findFormikFieldByName(form, "profile.company_size").exists())
assert.ok(findFormikFieldByName(form, "legal_address.city").exists())
assert.ok(form.find(".add-vat-id").exists())
assert.ok(form.find("button[type='submit']").exists())
})

;[true, false].forEach(isVatEnabled => {
it(`Validate that VAT ID is ${
isVatEnabled ? "enabled" : "disabled"
} for EditProfileForm`, () => {
const wrapper = renderForm(isVatEnabled)
const form = wrapper.find("Formik")
assert.equal(form.find(".add-vat-id").exists(), !isVatEnabled)
assert.equal(wrapper.find(`input[name="legal_address.vat_id"]`).exists(), isVatEnabled)
})
})

it(`validates that street address[0] is required`, async () => {
const wrapper = renderForm()
const street = wrapper.find(`input[name="legal_address.street_address[0]"]`)
Expand Down
30 changes: 28 additions & 2 deletions static/js/components/forms/ProfileFormFields.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export const legalAddressValidation = yup.object().shape({
.trim()
.matches(NAME_REGEX, NAME_REGEX_FAIL_MESSAGE)
.required(),
vat_id: yup
.string()
.label("VAT ID")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add min/max limits to the field here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by min/max limits like the character limit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 52e3aeb.

.max(30)
.trim(),
city: yup
.string()
.label("City")
Expand Down Expand Up @@ -127,15 +132,19 @@ type LegalAddressProps = {
setFieldValue: Function,
setFieldTouched: Function,
values: Object,
includePassword: boolean
includePassword: boolean,
isVatEnabled: boolean,
enableVatID: Function
}

export const LegalAddressFields = ({
countries,
setFieldValue,
setFieldTouched,
values,
includePassword
includePassword,
isVatEnabled,
enableVatID
}: LegalAddressProps) => (
<React.Fragment>
<div className="form-group">
Expand Down Expand Up @@ -176,6 +185,23 @@ export const LegalAddressFields = ({
/>
<ErrorMessage name="name" component={FormError} />
</div>
{isVatEnabled ? (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should write a test for this as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are just the fields used in RegisterDetailsPage and EditProfilePage. We handled tests for this in the respective pages i.e. RegisterDetailsPage and EditProfilePage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we do not have any existing tests for ProfileFormFields.js. I assume all of the tests are meant to be added for the pages themselves.

<div className="form-group">
<label htmlFor="legal_address.vat_id" className="font-weight-bold">
VAT ID
</label>
<Field type="text" name="legal_address.vat_id" className="form-control" />
<ErrorMessage name="legal_address.vat_id" component={FormError} />
</div>
) : (
<button
type="button"
className="add-vat-id"
onClick={enableVatID}
>
Add VAT ID
</button>
)}
{includePassword ? (
<div className="form-group">
<label htmlFor="password" className="font-weight-bold">
Expand Down
11 changes: 8 additions & 3 deletions static/js/components/forms/RegisterDetailsForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import type { Country } from "../../flow/authTypes"

type Props = {
onSubmit: Function,
countries: Array<Country>
countries: Array<Country>,
isVatEnabled: boolean,
enableVatID: Function,
}

const INITIAL_VALUES = {
Expand All @@ -25,11 +27,12 @@ const INITIAL_VALUES = {
city: "",
country: "",
state_or_territory: "",
postal_code: ""
postal_code: "",
vat_id: "",
}
}

const RegisterDetailsForm = ({ onSubmit, countries }: Props) => (
const RegisterDetailsForm = ({ onSubmit, countries, isVatEnabled, enableVatID }: Props) => (
<Formik
onSubmit={onSubmit}
validationSchema={legalAddressValidation.concat(passwordValidation)}
Expand All @@ -42,6 +45,8 @@ const RegisterDetailsForm = ({ onSubmit, countries }: Props) => (
setFieldTouched={setFieldTouched}
values={values}
includePassword={true}
isVatEnabled={isVatEnabled}
enableVatID={enableVatID}
/>
<div className="row submit-row no-gutters justify-content-end">
<button
Expand Down
18 changes: 15 additions & 3 deletions static/js/components/forms/RegisterDetailsForm_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import {
import { makeCountries } from "../../factories/user"

describe("RegisterDetailsForm", () => {
let sandbox, onSubmitStub
let sandbox, onSubmitStub, enableVatIDStub

const countries = makeCountries()

const renderForm = () =>
mount(<RegisterDetailsForm onSubmit={onSubmitStub} countries={countries} />)
const renderForm = (isVatEnabled = false) =>
mount(<RegisterDetailsForm onSubmit={onSubmitStub} countries={countries} isVatEnabled={isVatEnabled} enableVatID={enableVatIDStub}/>)

beforeEach(() => {
sandbox = sinon.createSandbox()
Expand All @@ -39,6 +39,18 @@ describe("RegisterDetailsForm", () => {
assert.ok(findFormikFieldByName(form, "name").exists())
assert.ok(findFormikFieldByName(form, "password").exists())
assert.ok(form.find("button[type='submit']").exists())
assert.ok(form.find(".add-vat-id").exists())
})

;[true, false].forEach(isVatEnabled => {
it(`Validate that VAT ID is ${
isVatEnabled ? "enabled" : "disabled"
} for RegisterDetailsForm`, () => {
const wrapper = renderForm(isVatEnabled)
const form = wrapper.find("Formik")
assert.equal(form.find(".add-vat-id").exists(), !isVatEnabled)
assert.equal(wrapper.find(`input[name="legal_address.vat_id"]`).exists(), isVatEnabled)
})
})

//
Expand Down
8 changes: 8 additions & 0 deletions static/js/containers/pages/ReceiptPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,14 @@ export class ReceiptPage extends React.Component<Props> {
{orderReceipt.purchaser.email}
</dd>
</dl>
{orderReceipt.purchaser.vat_id ? (
<dl>
<dt>VAT ID:</dt>
<dd id="purchaserVATNumber">
{orderReceipt.purchaser.vat_id}
</dd>
</dl>
) : null}
</div>
<h3>Payment Information</h3>
{orderReceipt.receipt ? (
Expand Down
Loading
Loading