diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 40aa7ab3148..a9f0a62dd9d 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -159,7 +159,7 @@ This section describes some noteworthy details on how certain features are imple
#### Motivation
The property agent may want to edit the details of a customer or property after adding it to the application. For example, the property agent may want to change the budget range of a customer after adding it to PropertyMatch.
-Or, the property agent may want to change the price of a property after adding it to PropertyMatch.
+Or, the property agent may want to change the budget of a property after adding it to PropertyMatch.
#### Implementation
The `EditCustomerCommand` and `EditPropertyCommand` classes extends the `Command` class. They are used to edit the details of a customer or property, respectively.
@@ -301,17 +301,17 @@ The following sequence diagram shows how the `FilterCustomerCommand` is executed
[Back to top](#table-of-contents)
#### Motivation
-The property agent may want to see a list of properties based on their budget. For example, the property agent may want to filter properties with price less than $1000000.
+The property agent may want to see a list of properties based on their budget. For example, the property agent may want to filter properties with budget less than $1000000.
Or, the property agent may want to see a list of properties based on the characteristics. For example, the property agent may want to filter pink properties.
-Or, the property agent may want to see a list of properties based on both price and characteristics to enhance productivity.
+Or, the property agent may want to see a list of properties based on both budget and characteristics to enhance productivity.
#### Implementation
The `FilterPropertyCommand` class extends the `Command` class. They are used to filter properties.
-The command allows the user to filter properties based on their price and/or characteristics. The commands expect at least one flag, either price or characteristics, to be used as a filter.
+The command allows the user to filter properties based on their budget and/or characteristics. The commands expect at least one flag, either budget or characteristics, to be used as a filter.
When the filter command is inputted, the `FilterPropertyCommandParser` class is used to parse the user input and create the respective `FilterPropertyCommand` objects.
When these created command objects are executed by the `LogicManager`, the `FilterPropertyCommand#execute(Model model)` methods are called. These methods will update the filtered property list in the `model` which will eventually update the properties shown in the UI, and return a `CommandResult` object.
-During this execution process, a new `PriceAndTagsInRangePredicate` object which is used as a predicate to check whether a property's price is lower and if the property has all the characteristics.
+During this execution process, a new `PriceAndTagsInRangePredicate` object which is used as a predicate to check whether a property's budget is lower and if the property has all the characteristics.
All properties will be tested using this `PriceAndTagsInRangePredicate`. Properties which satisfy this condition will be included into the `FilteredPropertyList` in the model.
#### Design Considerations
@@ -576,7 +576,7 @@ System: PropertyMatch address book
Actor: Property Agent
-1. Property agent fills in name, address, characteristics, number, price of property
+1. Property agent fills in name, address, characteristics, number, budget of property
2. Property agent adds property to address book
**Use Case: UC02 - Add customer**
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index a5deef2a21a..bc669b8a264 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -67,14 +67,14 @@ PropertyMatch is a desktop application for property agents who want to organise
Adds a property to the application.
-Format: `addprop n/NAME a/ADDRESS [c/CHARACTERISTIC] ph/number pr/price`
+Format: `addprop n/NAME a/ADDRESS [c/CHARACTERISTIC] ph/number pr/budget`
Parameter:
* `n/NAME` : The propName of the property (String)
* `a/ADDRESS` : The propAddress of the property (String)
* `c/CHARACTERISTIC` (Optional) : The characteristics of the property (String)
* `ph/NUMBER` : The contact number (Integer)
-* `pr/PRICE` : The price of the property in psf (Number)
+* `pr/PRICE` : The budget of the property in psf (Number)
Examples:
@@ -89,7 +89,7 @@ When command fails:
* `Missing propName parameter for add properties command` for missing propName parameter
* `Missing propAddress parameter for add properties command` for missing propAddress parameter
* `Missing number parameter for add properties command` for missing propName parameter
-* `Missing price parameter for add properties command` for missing price parameter
+* `Missing budget parameter for add properties command` for missing budget parameter
* `Invalid Command` for mispelling of command
### Adding a customer: `addcust`
@@ -278,7 +278,7 @@ When command fails:
Format: `filtercust [pr/PRICE] [c/CHARACTERISTIC]…`
Parameter:
-* `pr/PRICE` (optional) : The price of the property (Integer)
+* `pr/PRICE` (optional) : The budget of the property (Integer)
* `c/CHARACTERISTIC` (optional) : The characteristics of the property (String)
Notes:
@@ -340,7 +340,7 @@ When command fails: Invalid command for misspelling of command
| Action | Format, Examples |
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| **Addprop** | `addprop n/NAME a/ADDRESS [c/CHARACTERISTIC] ph/number pr/price` e.g., `addprop n/Property a/randomAddress c/bright;sunny;big;square ph/91135235 pr/5` |
+| **Addprop** | `addprop n/NAME a/ADDRESS [c/CHARACTERISTIC] ph/number pr/budget` e.g., `addprop n/Property a/randomAddress c/bright;sunny;big;square ph/91135235 pr/5` |
| **Addcust** | `addcust n/NAME p/PHONE e/EMAIL [b/BUDGET] [c/CHARACTERISTIC]` e.g., `addcust n/Fredy p/12345678 e/fredylawrence@gmail.com b/100000` |
| **Delprop** | `delprop INDEX` e.g., `delprop 3` |
| **Delcust** | `delcust INDEX` e.g., `delcust 3` |
diff --git a/docs/diagrams/FilterCustomerSequenceDiagram.puml b/docs/diagrams/FilterCustomerSequenceDiagram.puml
index b0dd2df0c8e..39908511bb9 100644
--- a/docs/diagrams/FilterCustomerSequenceDiagram.puml
+++ b/docs/diagrams/FilterCustomerSequenceDiagram.puml
@@ -7,6 +7,7 @@ participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
participant ":FilterCustomerCommandParser" as FilterCustomerCommandParser LOGIC_COLOR
participant ":Budget" as Budget LOGIC_COLOR
participant ":Tag" as Tag LOGIC_COLOR
+participant "descriptor:FilterCustomerDescriptor" as FilterCustomerDescriptor LOGIC_COLOR
participant ":BudgetAndTagsInRangePredicate" as BudgetAndTagsInRangePredicate LOGIC_COLOR
participant "command:FilterCustomerCommand" as FilterCustomerCommand LOGIC_COLOR
participant ":CommandResult" as CommandResult LOGIC_COLOR
@@ -32,6 +33,13 @@ deactivate FilterCustomerCommandParser
AddressBookParser -> FilterCustomerCommandParser : parse("b/100000 c/pink")
activate FilterCustomerCommandParser
+create FilterCustomerDescriptor
+FilterCustomerCommandParser -> FilterCustomerDescriptor: new FilterCustomerDescriptor
+activate FilterCustomerDescriptor
+
+FilterCustomerDescriptor --> FilterCustomerCommandParser: descriptor
+deactivate FilterCustomerDescriptor
+
create Budget
FilterCustomerCommandParser -> Budget: new Budget(100000)
activate Budget
@@ -39,6 +47,10 @@ activate Budget
Budget --> FilterCustomerCommandParser: budget
deactivate Budget
+FilterCustomerCommandParser -> FilterCustomerDescriptor: setBudget(budget)
+activate FilterCustomerDescriptor
+deactivate FilterCustomerDescriptor
+
create Tag
FilterCustomerCommandParser -> Tag: new Tag("pink")
activate Tag
@@ -46,13 +58,23 @@ activate Tag
Tag --> FilterCustomerCommandParser: tag
deactivate Tag
+FilterCustomerCommandParser -> FilterCustomerDescriptor: setTags(tags)
+activate FilterCustomerDescriptor
+deactivate FilterCustomerDescriptor
+
+FilterCustomerCommandParser -> FilterCustomerDescriptor: getPredicate()
+activate FilterCustomerDescriptor
+
create BudgetAndTagsInRangePredicate
-FilterCustomerCommandParser -> BudgetAndTagsInRangePredicate: new BudgetAndTagsInRangePredicate(budget, tags)
+FilterCustomerDescriptor -> BudgetAndTagsInRangePredicate: new BudgetAndTagsInRangePredicate(budget, tags)
activate BudgetAndTagsInRangePredicate
-BudgetAndTagsInRangePredicate --> FilterCustomerCommandParser: budgetAndTagsInRangePredicate
+BudgetAndTagsInRangePredicate --> FilterCustomerDescriptor: budgetAndTagsInRangePredicate
deactivate BudgetAndTagsInRangePredicate
+FilterCustomerDescriptor --> FilterCustomerCommandParser: budgetAndTagsInRangePredicate
+deactivate FilterCustomerDescriptor
+
create FilterCustomerCommand
FilterCustomerCommandParser -> FilterCustomerCommand : new FilterCustomerCommand(budgetAndTagsInRangePredicate)
activate FilterCustomerCommand
diff --git a/docs/images/FilterCustomerSequenceDiagram.png b/docs/images/FilterCustomerSequenceDiagram.png
index cb3c28dce91..a6b32059ac9 100644
Binary files a/docs/images/FilterCustomerSequenceDiagram.png and b/docs/images/FilterCustomerSequenceDiagram.png differ
diff --git a/src/main/java/seedu/address/logic/commands/FilterCustomerCommand.java b/src/main/java/seedu/address/logic/commands/FilterCustomerCommand.java
index 09717ebebfd..23fb6416221 100644
--- a/src/main/java/seedu/address/logic/commands/FilterCustomerCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FilterCustomerCommand.java
@@ -4,11 +4,20 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_BUDGET;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import seedu.address.commons.util.CollectionUtil;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
+import seedu.address.model.customer.Budget;
import seedu.address.model.customer.BudgetAndTagsInRangePredicate;
+import seedu.address.model.tag.Tag;
/**
* Filter all customers in the address book to the user based on specific tags and/or budget.
@@ -63,4 +72,82 @@ public String toString() {
.add("predicate", predicate)
.toString();
}
+
+ /**
+ * Stores the details to filter the customer with budget and tags.
+ */
+ public static class FilterCustomerDescriptor {
+ private Budget budget;
+ private Set tags;
+
+ public FilterCustomerDescriptor() {}
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public FilterCustomerDescriptor(FilterCustomerDescriptor toCopy) {
+ setBudget(toCopy.budget);
+ setTags(toCopy.tags);
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldFiltered() {
+ return CollectionUtil.isAnyNonNull(budget, tags);
+ }
+
+ public void setBudget(Budget budget) {
+ this.budget = budget;
+ }
+
+ public Optional getBudget() {
+ return Optional.ofNullable(budget);
+ }
+
+ /**
+ * Sets {@code tags} to this object's {@code tags}.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public void setTags(Set tags) {
+ this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ }
+
+ /**
+ * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code tags} is null.
+ */
+ public Optional> getTags() {
+ return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
+ }
+
+ public BudgetAndTagsInRangePredicate getPredicate() {
+ return new BudgetAndTagsInRangePredicate(budget, tags);
+ }
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FilterCustomerDescriptor)) {
+ return false;
+ }
+
+ FilterCustomerDescriptor otherFilterPropertyDescriptor = (FilterCustomerDescriptor) other;
+ return Objects.equals(budget, otherFilterPropertyDescriptor.budget)
+ && Objects.equals(tags, otherFilterPropertyDescriptor.tags);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("budget", budget)
+ .add("tags", tags)
+ .toString();
+ }
+ }
}
diff --git a/src/main/java/seedu/address/logic/commands/FilterPropertyCommand.java b/src/main/java/seedu/address/logic/commands/FilterPropertyCommand.java
index dcf317f74b8..4922c78a02f 100644
--- a/src/main/java/seedu/address/logic/commands/FilterPropertyCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FilterPropertyCommand.java
@@ -77,8 +77,8 @@ public String toString() {
* Stores the details to filter the property with price and tags.
*/
public static class FilterPropertyDescriptor {
- private Price price = null;
- private Set tags = null;
+ private Price price;
+ private Set tags;
public FilterPropertyDescriptor() {}
diff --git a/src/main/java/seedu/address/logic/parser/FilterCustomerCommandParser.java b/src/main/java/seedu/address/logic/parser/FilterCustomerCommandParser.java
index 948600891f8..7162c56bd38 100644
--- a/src/main/java/seedu/address/logic/parser/FilterCustomerCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/FilterCustomerCommandParser.java
@@ -1,18 +1,13 @@
package seedu.address.logic.parser;
-import static java.util.Objects.isNull;
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_BUDGET;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import java.util.Set;
-
import seedu.address.logic.commands.FilterCustomerCommand;
+import seedu.address.logic.commands.FilterCustomerCommand.FilterCustomerDescriptor;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.customer.Budget;
-import seedu.address.model.customer.BudgetAndTagsInRangePredicate;
-import seedu.address.model.tag.Tag;
/**
* Filters and lists all customers in address book whose budget and/or tags are selected.
@@ -26,24 +21,25 @@ public class FilterCustomerCommandParser implements Parser tags;
+ FilterCustomerDescriptor descriptor = new FilterCustomerDescriptor();
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_BUDGET, PREFIX_TAG);
argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_BUDGET);
if (argMultimap.getValue(PREFIX_BUDGET).isPresent()) {
- budget = ParserUtil.parseBudget(argMultimap.getValue(PREFIX_BUDGET).get());
+ descriptor.setBudget(ParserUtil.parseBudget(argMultimap.getValue(PREFIX_BUDGET).get()));
}
- tags = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
+ if (argMultimap.getValue(PREFIX_TAG).isPresent()) {
+ descriptor.setTags(ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)));
+ }
- if (isNull(budget) && tags.isEmpty()) {
+ if (!descriptor.isAnyFieldFiltered()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
FilterCustomerCommand.MESSAGE_USAGE));
}
- return new FilterCustomerCommand(new BudgetAndTagsInRangePredicate(budget, tags));
+ return new FilterCustomerCommand(descriptor.getPredicate());
}
}
diff --git a/src/main/resources/view/PropertyListCard.fxml b/src/main/resources/view/PropertyListCard.fxml
index 103dce8e6dc..b77c0f1fac5 100644
--- a/src/main/resources/view/PropertyListCard.fxml
+++ b/src/main/resources/view/PropertyListCard.fxml
@@ -30,7 +30,7 @@
-
+
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index ce2c34e913d..e3dc08aa022 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -16,6 +16,8 @@
import java.util.List;
import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.EditCustomerCommand.EditCustomerDescriptor;
+import seedu.address.logic.commands.FilterCustomerCommand.FilterCustomerDescriptor;
import seedu.address.logic.commands.FilterPropertyCommand.FilterPropertyDescriptor;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
@@ -23,6 +25,7 @@
import seedu.address.model.customer.Customer;
import seedu.address.model.customer.NameContainsKeywordsPredicate;
import seedu.address.testutil.EditCustomerDescriptorBuilder;
+import seedu.address.testutil.FilterCustomerDescriptorBuilder;
import seedu.address.testutil.FilterPropertyDescriptorBuilder;
/**
@@ -62,8 +65,10 @@ public class CommandTestUtil {
public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
- public static final EditCustomerCommand.EditCustomerDescriptor DESC_AMY;
- public static final EditCustomerCommand.EditCustomerDescriptor DESC_BOB;
+ public static final EditCustomerDescriptor DESC_AMY;
+ public static final EditCustomerDescriptor DESC_BOB;
+ public static final FilterCustomerDescriptor FILTER_CUSTOMER_DESCRIPTOR_AMY;
+ public static final FilterCustomerDescriptor FILTER_CUSTOMER_DESCRIPTOR_BOB;
static {
DESC_AMY = new EditCustomerDescriptorBuilder().withName(VALID_NAME_AMY)
@@ -72,6 +77,10 @@ public class CommandTestUtil {
DESC_BOB = new EditCustomerDescriptorBuilder().withName(VALID_NAME_BOB)
.withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withBudget(VALID_BUDGET_BOB)
.withTags(VALID_TAG_BIG, VALID_TAG_SQUARE).build();
+ FILTER_CUSTOMER_DESCRIPTOR_AMY = new FilterCustomerDescriptorBuilder().withBudget(VALID_BUDGET_AMY)
+ .withTags(VALID_TAG_SQUARE).build();
+ FILTER_CUSTOMER_DESCRIPTOR_BOB = new FilterCustomerDescriptorBuilder().withBudget(VALID_BUDGET_BOB)
+ .withTags(VALID_TAG_BIG, VALID_TAG_SQUARE).build();
}
// Properties
diff --git a/src/test/java/seedu/address/logic/commands/FilterCustomerCommandTest.java b/src/test/java/seedu/address/logic/commands/FilterCustomerCommandTest.java
index ed0ff0ed011..cccfae630c9 100644
--- a/src/test/java/seedu/address/logic/commands/FilterCustomerCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/FilterCustomerCommandTest.java
@@ -260,6 +260,27 @@ public void execute_withBudgetSomeTags_customerFound() {
assertEquals(Arrays.asList(BENSON), model.getFilteredCustomerList());
}
+ @Test
+ public void toStringMethod() {
+ String firstTagString = "square";
+ String secondTagString = "garden";
+
+ Tag firstTag = new Tag(firstTagString);
+ Tag secondTag = new Tag(secondTagString);
+
+ Set tags = new HashSet<>();
+ tags.add(firstTag);
+ tags.add(secondTag);
+
+ String budgetString = "100000000";
+ Budget budget = new Budget(budgetString);
+
+ BudgetAndTagsInRangePredicate predicate = new BudgetAndTagsInRangePredicate(budget, tags);
+ FilterCustomerCommand filterCustomerCommand = new FilterCustomerCommand(predicate);
+ String expected = FilterCustomerCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, filterCustomerCommand.toString());
+ }
+
private FilterCustomerCommand preparePredicate(String message) {
try {
return (new FilterCustomerCommandParser()).parse(message);
diff --git a/src/test/java/seedu/address/logic/commands/FilterCustomerDescriptorTest.java b/src/test/java/seedu/address/logic/commands/FilterCustomerDescriptorTest.java
new file mode 100644
index 00000000000..eb03bf7bf3e
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FilterCustomerDescriptorTest.java
@@ -0,0 +1,55 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.FILTER_CUSTOMER_DESCRIPTOR_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.FILTER_CUSTOMER_DESCRIPTOR_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_BUDGET_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_BIG;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.FilterCustomerCommand.FilterCustomerDescriptor;
+import seedu.address.testutil.FilterCustomerDescriptorBuilder;
+
+public class FilterCustomerDescriptorTest {
+ @Test
+ public void equals() {
+ // same values -> returns true
+ FilterCustomerDescriptor descriptorWithSameValues =
+ new FilterCustomerDescriptor(FILTER_CUSTOMER_DESCRIPTOR_AMY);
+ assertTrue(FILTER_CUSTOMER_DESCRIPTOR_AMY.equals(descriptorWithSameValues));
+
+ // same object -> returns true
+ assertTrue(FILTER_CUSTOMER_DESCRIPTOR_AMY.equals(FILTER_CUSTOMER_DESCRIPTOR_AMY));
+
+ // null -> returns false
+ assertFalse(FILTER_CUSTOMER_DESCRIPTOR_AMY.equals(null));
+
+ // different types -> returns false
+ assertFalse(FILTER_CUSTOMER_DESCRIPTOR_AMY.equals(5));
+
+ // different values -> returns false
+ assertFalse(FILTER_CUSTOMER_DESCRIPTOR_AMY.equals(FILTER_CUSTOMER_DESCRIPTOR_BOB));
+
+ // different price -> returns false
+ FilterCustomerDescriptor editedAmy = new FilterCustomerDescriptorBuilder(FILTER_CUSTOMER_DESCRIPTOR_AMY)
+ .withBudget(VALID_BUDGET_BOB).build();
+ assertFalse(FILTER_CUSTOMER_DESCRIPTOR_AMY.equals(editedAmy));
+
+ // different tags -> returns false
+ editedAmy = new FilterCustomerDescriptorBuilder(FILTER_CUSTOMER_DESCRIPTOR_AMY)
+ .withTags(VALID_TAG_BIG).build();
+ assertFalse(FILTER_CUSTOMER_DESCRIPTOR_AMY.equals(editedAmy));
+ }
+
+ @Test
+ public void toStringMethod() {
+ FilterCustomerDescriptor filterPropertyDescriptor = new FilterCustomerDescriptor();
+ String expected = FilterCustomerDescriptor.class.getCanonicalName() + "{budget="
+ + filterPropertyDescriptor.getBudget().orElse(null) + ", tags="
+ + filterPropertyDescriptor.getTags().orElse(null) + "}";
+ assertEquals(expected, filterPropertyDescriptor.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/testutil/FilterCustomerDescriptorBuilder.java b/src/test/java/seedu/address/testutil/FilterCustomerDescriptorBuilder.java
new file mode 100644
index 00000000000..eca47b6a821
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/FilterCustomerDescriptorBuilder.java
@@ -0,0 +1,56 @@
+package seedu.address.testutil;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.FilterCustomerCommand.FilterCustomerDescriptor;
+import seedu.address.model.customer.Budget;
+import seedu.address.model.customer.Customer;
+import seedu.address.model.tag.Tag;
+
+/**
+ * A utility class to help with building FilterCustomerDescriptor objects.
+ */
+public class FilterCustomerDescriptorBuilder {
+
+ private FilterCustomerDescriptor descriptor;
+
+ public FilterCustomerDescriptorBuilder() {
+ descriptor = new FilterCustomerDescriptor();
+ }
+
+ public FilterCustomerDescriptorBuilder(FilterCustomerDescriptor descriptor) {
+ this.descriptor = new FilterCustomerDescriptor(descriptor);
+ }
+
+ /**
+ * Returns an {@code FilterCustomerDescriptorBuilder} with fields containing {@code customer}'s details
+ */
+ public FilterCustomerDescriptorBuilder(Customer customer) {
+ descriptor.setBudget(customer.getBudget());
+ descriptor.setTags(customer.getTags());
+ }
+
+ /**
+ * Sets the {@code Budget} of the {@code EditCustomerDescriptor} that we are building.
+ */
+ public FilterCustomerDescriptorBuilder withBudget(String budget) {
+ descriptor.setBudget(new Budget(budget));
+ return this;
+ }
+
+ /**
+ * Parses the {@code tags} into a {@code Set} and set it to the {@code EditCustomerDescriptor}
+ * that we are building.
+ */
+ public FilterCustomerDescriptorBuilder withTags(String... tags) {
+ Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet());
+ descriptor.setTags(tagSet);
+ return this;
+ }
+
+ public FilterCustomerDescriptor build() {
+ return descriptor;
+ }
+}
diff --git a/src/test/java/seedu/address/testutil/FilterPropertyDescriptorBuilder.java b/src/test/java/seedu/address/testutil/FilterPropertyDescriptorBuilder.java
index c97209bedd9..da30a467f06 100644
--- a/src/test/java/seedu/address/testutil/FilterPropertyDescriptorBuilder.java
+++ b/src/test/java/seedu/address/testutil/FilterPropertyDescriptorBuilder.java
@@ -33,7 +33,7 @@ public FilterPropertyDescriptorBuilder(Property property) {
}
/**
- * Sets the {@code Price} of the {@code EditpropertyDescriptor} that we are building.
+ * Sets the {@code Price} of the {@code EditPropertyDescriptor} that we are building.
*/
public FilterPropertyDescriptorBuilder withPrice(String price) {
descriptor.setPrice(new Price(price));
@@ -41,7 +41,7 @@ public FilterPropertyDescriptorBuilder withPrice(String price) {
}
/**
- * Parses the {@code tags} into a {@code Set} and set it to the {@code EditpropertyDescriptor}
+ * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPropertyDescriptor}
* that we are building.
*/
public FilterPropertyDescriptorBuilder withTags(String... tags) {