This is an automated email from the ASF dual-hosted git repository.

winterhazel pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new a73cc9a22c0 Improve Quota Statement (#10506)
a73cc9a22c0 is described below

commit a73cc9a22c09954c1631c36d2a11064764cc8355
Author: julien-vaz <[email protected]>
AuthorDate: Wed Apr 29 21:09:13 2026 -0300

    Improve Quota Statement (#10506)
    
    * Improve Quota Statement
    
    * Removes unused import
    
    * Fix QuotaUsageJoinDao, QuotaResponseBuilderImpl, QuotaServiceImpl e 
QuotaServiceImplTest
    
    * Reorganize imports
    
    * Updates QuotaStatementCmd responseBuilder scope to default
    
    * Fix log4j syntax
    
    * Address reviews + other improvements
    
    * Add missing SQL scripts and injections
    
    * Change accountid and domainid logic + add unit tests
    
    * Rename QuotaUsageDetail to QuotaTariffUsage
    
    * Fix out of bounds exception
    
    ---------
    
    Co-authored-by: Julien Hervot de Mattos Vaz <[email protected]>
    Co-authored-by: Fabricio Duarte <[email protected]>
---
 .../org/apache/cloudstack/api/ApiConstants.java    |   6 +
 .../resources/META-INF/db/schema-42210to42300.sql  |  10 +
 .../db/views/cloud_usage.quota_usage_view.sql      |  35 +++
 .../java/com/cloud/utils/db/SearchCriteria.java    |   6 +
 .../cloudstack/quota/dao/QuotaTariffUsageDao.java  |  29 ++
 .../quota/dao/QuotaTariffUsageDaoImpl.java         |  56 ++++
 .../cloudstack/quota/dao/QuotaUsageJoinDao.java    |  31 ++
 .../quota/dao/QuotaUsageJoinDaoImpl.java           |  94 ++++++
 .../cloudstack/quota/vo/QuotaTariffUsageVO.java    |  86 ++++++
 .../cloudstack/quota/vo/QuotaUsageJoinVO.java      | 179 ++++++++++++
 .../cloudstack/quota/vo/QuotaUsageResourceVO.java  |  62 ++++
 .../quota/spring-framework-quota-context.xml       |   4 +-
 .../cloudstack/api/command/QuotaStatementCmd.java  |  74 +++--
 .../api/response/QuotaResponseBuilder.java         |   5 +-
 .../api/response/QuotaResponseBuilderImpl.java     | 322 +++++++++++++++------
 .../QuotaStatementItemResourceResponse.java        |  61 ++++
 .../api/response/QuotaStatementItemResponse.java   |  69 ++---
 .../api/response/QuotaStatementResponse.java       |  65 ++---
 .../org/apache/cloudstack/quota/QuotaService.java  |   4 +-
 .../apache/cloudstack/quota/QuotaServiceImpl.java  |  30 +-
 .../api/command/QuotaStatementCmdTest.java         |  23 +-
 .../api/response/QuotaResponseBuilderImplTest.java | 199 +++++++++++++
 .../cloudstack/quota/QuotaServiceImplTest.java     |  10 +-
 23 files changed, 1212 insertions(+), 248 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java 
b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 7eae16a2a37..6fb7cb0612c 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -157,6 +157,7 @@ public class ApiConstants {
     public static final String CUSTOM_ID = "customid";
     public static final String CUSTOM_ACTION_ID = "customactionid";
     public static final String CUSTOM_JOB_ID = "customjobid";
+    public static final String CURRENCY = "currency";
     public static final String CURRENT_START_IP = "currentstartip";
     public static final String CURRENT_END_IP = "currentendip";
     public static final String ENCRYPT = "encrypt";
@@ -541,6 +542,7 @@ public class ApiConstants {
     public static final String SESSIONKEY = "sessionkey";
     public static final String SHOW_CAPACITIES = "showcapacities";
     public static final String SHOW_REMOVED = "showremoved";
+    public static final String SHOW_RESOURCES = "showresources";
     public static final String SHOW_RESOURCE_ICON = "showicon";
     public static final String SHOW_INACTIVE = "showinactive";
     public static final String SHOW_UNIQUE = "showunique";
@@ -606,9 +608,11 @@ public class ApiConstants {
     public static final String TENANT_NAME = "tenantname";
     public static final String TOTAL = "total";
     public static final String TOTAL_SUBNETS = "totalsubnets";
+    public static final String TOTAL_QUOTA = "totalquota";
     public static final String TYPE = "type";
     public static final String TRUST_STORE = "truststore";
     public static final String TRUST_STORE_PASSWORD = "truststorepass";
+    public static final String UNIT = "unit";
     public static final String URL = "url";
     public static final String USAGE_INTERFACE = "usageinterface";
     public static final String USED = "used";
@@ -1300,6 +1304,8 @@ public class ApiConstants {
     public static final String OBJECT_LOCKING = "objectlocking";
     public static final String ENCRYPTION = "encryption";
     public static final String QUOTA = "quota";
+    public static final String QUOTA_CONSUMED = "quotaconsumed";
+    public static final String QUOTA_USAGE = "quotausage";
     public static final String ACCESS_KEY = "accesskey";
 
     public static final String SOURCE_NAT_IP = "sourcenatipaddress";
diff --git 
a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql 
b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
index 4cb9eb7cb2c..c0feb06e76a 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
@@ -117,3 +117,13 @@ CALL 
`cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','conserve_mode', 'tin
 
 --- Disable/enable NICs
 CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT 
NULL DEFAULT 1 COMMENT ''Indicates whether the NIC is enabled or not'' ');
+
+--- Quota tariff/usage mapping
+CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_tariff_usage` (
+    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+    `tariff_id` bigint(20) unsigned NOT NULL COMMENT 'ID of the tariff of the 
Quota usage detail calculated, foreign key to quota_tariff table',
+    `quota_usage_id` bigint(20) unsigned NOT NULL COMMENT 'ID of the 
aggregation of Quota usage details, foreign key to quota_usage table',
+    `quota_used` decimal(20,8) NOT NULL COMMENT 'Amount of quota used',
+    PRIMARY KEY (`id`),
+    CONSTRAINT `fk_quota_tariff_usage__tariff_id` FOREIGN KEY (`tariff_id`) 
REFERENCES `cloud_usage`.`quota_tariff` (`id`),
+    CONSTRAINT `fk_quota_tariff_usage__quota_usage_id` FOREIGN KEY 
(`quota_usage_id`) REFERENCES `cloud_usage`.`quota_usage` (`id`));
diff --git 
a/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_usage_view.sql
 
b/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_usage_view.sql
new file mode 100644
index 00000000000..7ac001384e8
--- /dev/null
+++ 
b/engine/schema/src/main/resources/META-INF/db/views/cloud_usage.quota_usage_view.sql
@@ -0,0 +1,35 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License.  You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied.  See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+-- VIEW `cloud_usage`.`quota_usage_view`;
+
+DROP VIEW IF EXISTS `cloud_usage`.`quota_usage_view`;
+CREATE VIEW `cloud_usage`.`quota_usage_view` AS
+SELECT  qu.id,
+        qu.usage_item_id,
+        qu.zone_id,
+        qu.account_id,
+        qu.domain_id,
+        qu.usage_type,
+        qu.quota_used,
+        qu.start_date,
+        qu.end_date,
+        cu.usage_id AS resource_id,
+        cu.network_id as network_id,
+        cu.offering_id as offering_id
+FROM    `cloud_usage`.`quota_usage` qu
+INNER   JOIN `cloud_usage`.`cloud_usage` cu ON (cu.id = qu.usage_item_id);
diff --git a/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java 
b/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java
index 15807eb26d4..3323d5c4d82 100644
--- a/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java
+++ b/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java
@@ -205,6 +205,12 @@ public class SearchCriteria<K> {
 
     }
 
+    public void setJoinParametersIfNotNull(String joinName, String 
conditionName, Object... params) {
+        if (ArrayUtils.isNotEmpty(params) && (params.length > 1 || params[0] 
!= null)) {
+            setJoinParameters(joinName, conditionName, params);
+        }
+    }
+
     public SearchCriteria<?> getJoin(String joinName) {
         return _joins.get(joinName).getT();
     }
diff --git 
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffUsageDao.java
 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffUsageDao.java
new file mode 100644
index 00000000000..9684ca117b5
--- /dev/null
+++ 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffUsageDao.java
@@ -0,0 +1,29 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
+import java.util.List;
+
+public interface QuotaTariffUsageDao extends GenericDao<QuotaTariffUsageVO, 
Long> {
+
+    void persistQuotaTariffUsage(QuotaTariffUsageVO quotaTariffUsage);
+
+    List<QuotaTariffUsageVO> listQuotaTariffUsages(Long quotaUsageId);
+
+}
diff --git 
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffUsageDaoImpl.java
 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffUsageDaoImpl.java
new file mode 100644
index 00000000000..556f552fed6
--- /dev/null
+++ 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffUsageDaoImpl.java
@@ -0,0 +1,56 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
+import org.springframework.stereotype.Component;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionLegacy;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+
+@Component
+public class QuotaTariffUsageDaoImpl extends 
GenericDaoBase<QuotaTariffUsageVO, Long> implements QuotaTariffUsageDao {
+    private SearchBuilder<QuotaTariffUsageVO> searchQuotaTariffUsages;
+
+    @PostConstruct
+    public void init() {
+        searchQuotaTariffUsages = createSearchBuilder();
+        searchQuotaTariffUsages.and("quotaUsageId", 
searchQuotaTariffUsages.entity().getQuotaUsageId(), SearchCriteria.Op.EQ);
+        searchQuotaTariffUsages.done();
+    }
+
+    @Override
+    public void persistQuotaTariffUsage(final QuotaTariffUsageVO 
quotaTariffUsage) {
+        logger.trace("Persisting quota tariff usage [{}].", quotaTariffUsage);
+        Transaction.execute(TransactionLegacy.USAGE_DB, 
(TransactionCallback<QuotaTariffUsageVO>) status -> persist(quotaTariffUsage));
+    }
+
+    @Override
+    public List<QuotaTariffUsageVO> listQuotaTariffUsages(Long quotaUsageId) {
+        SearchCriteria<QuotaTariffUsageVO> sc = 
searchQuotaTariffUsages.create();
+        sc.setParameters("quotaUsageId", quotaUsageId);
+        return Transaction.execute(TransactionLegacy.USAGE_DB, 
(TransactionCallback<List<QuotaTariffUsageVO>>) status -> listBy(sc));
+    }
+
+}
diff --git 
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaUsageJoinDao.java
 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaUsageJoinDao.java
new file mode 100644
index 00000000000..126fa11413f
--- /dev/null
+++ 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaUsageJoinDao.java
@@ -0,0 +1,31 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
+
+import java.util.Date;
+import java.util.List;
+
+public interface QuotaUsageJoinDao extends GenericDao<QuotaUsageJoinVO, Long> {
+
+    List<QuotaUsageJoinVO> findQuotaUsage(Long accountId, Long domainId, 
Integer usageType, Long resourceId, Long networkId, Long offeringId, Date 
startDate, Date endDate, Long tariffId);
+
+}
diff --git 
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaUsageJoinDaoImpl.java
 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaUsageJoinDaoImpl.java
new file mode 100644
index 00000000000..b98ea2b3a5d
--- /dev/null
+++ 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaUsageJoinDaoImpl.java
@@ -0,0 +1,94 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionLegacy;
+import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
+import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.util.Date;
+import java.util.List;
+
+@Component
+public class QuotaUsageJoinDaoImpl extends GenericDaoBase<QuotaUsageJoinVO, 
Long> implements QuotaUsageJoinDao {
+
+    private SearchBuilder<QuotaUsageJoinVO> searchQuotaUsages;
+
+    private SearchBuilder<QuotaUsageJoinVO> searchQuotaUsagesJoinTariffUsages;
+
+    @Inject
+    private QuotaTariffUsageDao quotaTariffUsageDao;
+
+    @PostConstruct
+    public void init() {
+        searchQuotaUsages = createSearchBuilder();
+        prepareQuotaUsageSearchBuilder(searchQuotaUsages);
+        searchQuotaUsages.done();
+
+        SearchBuilder<QuotaTariffUsageVO> searchQuotaTariffUsages = 
quotaTariffUsageDao.createSearchBuilder();
+        searchQuotaTariffUsages.and("tariffId", 
searchQuotaTariffUsages.entity().getTariffId(), SearchCriteria.Op.EQ);
+        searchQuotaUsagesJoinTariffUsages = createSearchBuilder();
+        prepareQuotaUsageSearchBuilder(searchQuotaUsagesJoinTariffUsages);
+        searchQuotaUsagesJoinTariffUsages.join("searchQuotaTariffUsages", 
searchQuotaTariffUsages, searchQuotaUsagesJoinTariffUsages.entity().getId(),
+                searchQuotaTariffUsages.entity().getQuotaUsageId(), 
JoinBuilder.JoinType.INNER);
+        searchQuotaUsagesJoinTariffUsages.done();
+    }
+
+    private void 
prepareQuotaUsageSearchBuilder(SearchBuilder<QuotaUsageJoinVO> searchBuilder) {
+        searchBuilder.and("accountId", searchBuilder.entity().getAccountId(), 
SearchCriteria.Op.EQ);
+        searchBuilder.and("domainId", searchBuilder.entity().getDomainId(), 
SearchCriteria.Op.EQ);
+        searchBuilder.and("usageType", searchBuilder.entity().getUsageType(), 
SearchCriteria.Op.EQ);
+        searchBuilder.and("resourceId", 
searchBuilder.entity().getResourceId(), SearchCriteria.Op.EQ);
+        searchBuilder.and("networkId", searchBuilder.entity().getNetworkId(), 
SearchCriteria.Op.EQ);
+        searchBuilder.and("offeringId", 
searchBuilder.entity().getOfferingId(), SearchCriteria.Op.EQ);
+        searchBuilder.and("startDate", searchBuilder.entity().getStartDate(), 
SearchCriteria.Op.BETWEEN);
+        searchBuilder.and("endDate", searchBuilder.entity().getEndDate(), 
SearchCriteria.Op.BETWEEN);
+    }
+
+    @Override
+    public List<QuotaUsageJoinVO> findQuotaUsage(Long accountId, Long 
domainId, Integer usageType, Long resourceId, Long networkId, Long offeringId, 
Date startDate, Date endDate, Long tariffId) {
+        SearchCriteria<QuotaUsageJoinVO> sc = tariffId == null ? 
searchQuotaUsages.create() : searchQuotaUsagesJoinTariffUsages.create();
+
+        sc.setParametersIfNotNull("accountId", accountId);
+        sc.setParametersIfNotNull("domainId", domainId);
+        sc.setParametersIfNotNull("usageType", usageType);
+        sc.setParametersIfNotNull("resourceId", resourceId);
+        sc.setParametersIfNotNull("networkId", networkId);
+        sc.setParametersIfNotNull("offeringId", offeringId);
+
+        if (ObjectUtils.allNotNull(startDate, endDate)) {
+            sc.setParameters("startDate", startDate, endDate);
+            sc.setParameters("endDate", startDate, endDate);
+        }
+
+        sc.setJoinParametersIfNotNull("searchQuotaTariffUsages", "tariffId", 
tariffId);
+
+        return Transaction.execute(TransactionLegacy.USAGE_DB, 
(TransactionCallback<List<QuotaUsageJoinVO>>) status -> listBy(sc));
+    }
+
+}
diff --git 
a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffUsageVO.java
 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffUsageVO.java
new file mode 100644
index 00000000000..4fa9e771713
--- /dev/null
+++ 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffUsageVO.java
@@ -0,0 +1,86 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.quota.vo;
+
+import java.math.BigDecimal;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import org.apache.cloudstack.api.InternalIdentity;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+@Entity
+@Table(name = "quota_tariff_usage")
+public class QuotaTariffUsageVO implements InternalIdentity {
+    @Id
+    @Column(name = "id")
+    private Long id;
+
+    @Column(name = "tariff_id")
+    private Long tariffId;
+
+    @Column(name = "quota_usage_id")
+    private Long quotaUsageId;
+
+    @Column(name = "quota_used")
+    private BigDecimal quotaUsed;
+
+    public QuotaTariffUsageVO() {
+        quotaUsed = new BigDecimal(0);
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    public Long getTariffId() {
+        return tariffId;
+    }
+
+    public Long getQuotaUsageId() {
+        return quotaUsageId;
+    }
+
+    public BigDecimal getQuotaUsed() {
+        return quotaUsed;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public void setTariffId(Long tariffId) {
+        this.tariffId = tariffId;
+    }
+
+    public void setQuotaUsageId(Long quotaUsageId) {
+        this.quotaUsageId = quotaUsageId;
+    }
+
+    public void setQuotaUsed(BigDecimal quotaUsed) {
+        this.quotaUsed = quotaUsed;
+    }
+
+    @Override
+    public String toString() {
+        return new ReflectionToStringBuilder(this, 
ToStringStyle.JSON_STYLE).toString();
+    }
+}
diff --git 
a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaUsageJoinVO.java
 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaUsageJoinVO.java
new file mode 100644
index 00000000000..df9577e23c3
--- /dev/null
+++ 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaUsageJoinVO.java
@@ -0,0 +1,179 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.quota.vo;
+
+import org.apache.cloudstack.api.InternalIdentity;
+import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Entity
+@Table(name = "quota_usage_view")
+public class QuotaUsageJoinVO implements InternalIdentity {
+
+    @Id
+    @Column(name = "id", updatable = false, nullable = false)
+    private Long id;
+
+    @Column(name = "zone_id")
+    private Long zoneId = null;
+
+    @Column(name = "account_id")
+    private Long accountId = null;
+
+    @Column(name = "domain_id")
+    private Long domainId = null;
+
+    @Column(name = "usage_item_id")
+    private Long usageItemId;
+
+    @Column(name = "usage_type")
+    private int usageType;
+
+    @Column(name = "quota_used")
+    private BigDecimal quotaUsed;
+
+    @Column(name = "start_date")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    private Date startDate = null;
+
+    @Column(name = "end_date")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    private Date endDate = null;
+
+    @Column(name = "resource_id")
+    private Long resourceId = null;
+
+    @Column(name = "network_id")
+    private Long networkId = null;
+
+    @Column(name = "offering_id")
+    private Long offeringId = null;
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getZoneId() {
+        return zoneId;
+    }
+
+    public void setZoneId(Long zoneId) {
+        this.zoneId = zoneId;
+    }
+
+    public Long getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(Long accountId) {
+        this.accountId = accountId;
+    }
+
+    public Long getDomainId() {
+        return domainId;
+    }
+
+    public void setDomainId(Long domainId) {
+        this.domainId = domainId;
+    }
+
+    public Long getUsageItemId() {
+        return usageItemId;
+    }
+
+    public void setUsageItemId(Long usageItemId) {
+        this.usageItemId = usageItemId;
+    }
+
+    public int getUsageType() {
+        return usageType;
+    }
+
+    public void setUsageType(int usageType) {
+        this.usageType = usageType;
+    }
+
+    public BigDecimal getQuotaUsed() {
+        return quotaUsed;
+    }
+
+    public void setQuotaUsed(BigDecimal quotaUsed) {
+        this.quotaUsed = quotaUsed;
+    }
+
+    public Date getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(Date startDate) {
+        this.startDate = startDate;
+    }
+
+    public Date getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(Date endDate) {
+        this.endDate = endDate;
+    }
+
+    public Long getResourceId() {
+        return resourceId;
+    }
+
+    public void setResourceId(Long resourceId) {
+        this.resourceId = resourceId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public void setNetworkId(Long networkId) {
+        this.networkId = networkId;
+    }
+
+    public Long getOfferingId() {
+        return offeringId;
+    }
+
+    public void setOfferingId(Long offeringId) {
+        this.offeringId = offeringId;
+    }
+
+    public QuotaUsageJoinVO () {
+    }
+
+    @Override
+    public String toString() {
+        return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, 
"id", "zoneId", "accountId", "domainId", "usageItemId", "usageType", 
"quotaUsed", "startDate",
+                "endDate", "resourceId");
+    }
+}
diff --git 
a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaUsageResourceVO.java
 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaUsageResourceVO.java
new file mode 100644
index 00000000000..92482423100
--- /dev/null
+++ 
b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaUsageResourceVO.java
@@ -0,0 +1,62 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package org.apache.cloudstack.quota.vo;
+
+import java.util.Date;
+
+public class QuotaUsageResourceVO {
+    private String uuid;
+    private String name;
+    private Date removed;
+
+    public String getUuid() {
+        return uuid;
+    }
+
+    public void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Date getRemoved() {
+        return removed;
+    }
+
+    public void setRemoved(Date removed) {
+        this.removed = removed;
+    }
+
+    public boolean isRemoved() {
+        return this.removed != null;
+    }
+
+    public QuotaUsageResourceVO(String uuid, String name, Date removed) {
+        this.uuid = uuid;
+        this.name = name;
+        this.removed = removed;
+    }
+}
diff --git 
a/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
 
b/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
index 304b23b7220..5ca2679c388 100644
--- 
a/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
+++ 
b/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
@@ -26,7 +26,9 @@
        <bean id="QuotaEmailTemplatesDao"
                
class="org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDaoImpl" />
        <bean id="QuotaUsageDao" 
class="org.apache.cloudstack.quota.dao.QuotaUsageDaoImpl" />
-    <bean id="UserVmDetailsDao" 
class="org.apache.cloudstack.quota.dao.VMInstanceDetailsDaoImpl" />
+       <bean id="QuotaUsageJoinDao" 
class="org.apache.cloudstack.quota.dao.QuotaUsageJoinDaoImpl"/>
+       <bean id="QuotaTariffUsageDao" 
class="org.apache.cloudstack.quota.dao.QuotaTariffUsageDaoImpl" />
+       <bean id="UserVmDetailsDao" 
class="org.apache.cloudstack.quota.dao.VMInstanceDetailsDaoImpl" />
 
        <bean id="QuotaManager" 
class="org.apache.cloudstack.quota.QuotaManagerImpl" />
     <bean id="QuotaAlertManager" 
class="org.apache.cloudstack.quota.QuotaAlertManagerImpl" />
diff --git 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaStatementCmd.java
 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaStatementCmd.java
index d3bd3868ed1..bfe26a9f425 100644
--- 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaStatementCmd.java
+++ 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaStatementCmd.java
@@ -17,7 +17,6 @@
 package org.apache.cloudstack.api.command;
 
 import java.util.Date;
-import java.util.List;
 
 import javax.inject.Inject;
 
@@ -28,24 +27,25 @@ import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.response.AccountResponse;
 import org.apache.cloudstack.api.response.DomainResponse;
+import org.apache.cloudstack.api.response.ProjectResponse;
 import org.apache.cloudstack.api.response.QuotaResponseBuilder;
 import org.apache.cloudstack.api.response.QuotaStatementItemResponse;
 import org.apache.cloudstack.api.response.QuotaStatementResponse;
-import org.apache.cloudstack.quota.vo.QuotaUsageVO;
 
-import com.cloud.user.Account;
+import org.apache.commons.lang3.ObjectUtils;
 
-@APICommand(name = "quotaStatement", responseObject = 
QuotaStatementItemResponse.class, description = "Create a quota statement", 
since = "4.7.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = 
false,
-        httpMethod = "GET")
+@APICommand(name = "quotaStatement", responseObject = 
QuotaStatementItemResponse.class, description = "Create a Quota statement for 
the provided Account, Project, or Domain.",
+        since = "4.7.0", requestHasSensitiveInfo = false, 
responseHasSensitiveInfo = false, httpMethod = "GET")
 public class QuotaStatementCmd extends BaseCmd {
 
-
-
-    @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, 
required = true, description = "Optional, Account Id for which statement needs 
to be generated")
+    @ACL
+    @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING,
+            description = "Name of the Account for which the Quota statement 
will be generated. Deprecated, please use accountid instead.")
     private String accountName;
 
     @ACL
-    @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, 
required = true, entityType = DomainResponse.class, description = "Optional, If 
domain Id is given and the caller is domain admin then the statement is 
generated for domain.")
+    @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, 
entityType = DomainResponse.class,
+            description = "ID of the Domain for which the Quota statement will 
be generated. May be used individually or with account.")
     private Long domainId;
 
     @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required 
= true, description = "End of the period of the Quota statement. " +
@@ -56,15 +56,25 @@ public class QuotaStatementCmd extends BaseCmd {
             ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS)
     private Date startDate;
 
-    @Parameter(name = ApiConstants.TYPE, type = CommandType.INTEGER, 
description = "List quota usage records for the specified usage type")
+    @Parameter(name = ApiConstants.TYPE, type = CommandType.INTEGER,
+            description = "Consider only Quota usage records for the specified 
usage type in the statement.")
     private Integer usageType;
 
     @ACL
-    @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, 
entityType = AccountResponse.class, description = "List usage records for the 
specified Account")
+    @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, 
entityType = AccountResponse.class,
+            description = "ID of the Account for which the Quota statement 
will be generated. Can not be specified with projectid.")
     private Long accountId;
 
+    @ACL
+    @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, 
entityType = ProjectResponse.class,
+            description = "ID of the Project for which the Quota statement 
will be generated. Can not be specified with accountid.", since = "4.23.0")
+    private Long projectId;
+
+    @Parameter(name = ApiConstants.SHOW_RESOURCES, type = CommandType.BOOLEAN, 
description = "List the resources of each Quota type in the period.", since = 
"4.23.0")
+    private boolean showResources;
+
     @Inject
-    private QuotaResponseBuilder _responseBuilder;
+    QuotaResponseBuilder responseBuilder;
 
     public Long getAccountId() {
         return accountId;
@@ -99,43 +109,47 @@ public class QuotaStatementCmd extends BaseCmd {
     }
 
     public Date getEndDate() {
-        return _responseBuilder.startOfNextDay(endDate == null ? new Date() : 
new Date(endDate.getTime()));
+        return endDate;
     }
 
     public void setEndDate(Date endDate) {
-        this.endDate = endDate == null ? null : new Date(endDate.getTime());
+        this.endDate = endDate;
     }
 
     public Date getStartDate() {
-        return startDate == null ? null : new Date(startDate.getTime());
+        return startDate;
     }
 
     public void setStartDate(Date startDate) {
-        this.startDate = startDate == null ? null : new 
Date(startDate.getTime());
+        this.startDate = startDate;
+    }
+
+    public boolean isShowResources() {
+        return showResources;
+    }
+
+    public void setShowResources(boolean showResources) {
+        this.showResources = showResources;
+    }
+
+    public Long getProjectId() {
+        return projectId;
     }
 
     @Override
     public long getEntityOwnerId() {
-        if (accountId != null) {
-            return accountId;
+        if (ObjectUtils.allNull(accountId, accountName, projectId)) {
+            return -1;
         }
-        Account activeAccountByName = 
_accountService.getActiveAccountByName(accountName, domainId);
-        if (activeAccountByName != null) {
-            return activeAccountByName.getAccountId();
-        }
-        return Account.ACCOUNT_ID_SYSTEM;
+        return _accountService.finalizeAccountId(accountId, accountName, 
domainId, projectId);
     }
 
     @Override
     public void execute() {
-        List<QuotaUsageVO> quotaUsage = _responseBuilder.getQuotaUsage(this);
-
-        QuotaStatementResponse response = 
_responseBuilder.createQuotaStatementResponse(quotaUsage);
-        response.setStartDate(startDate == null ? null : new 
Date(startDate.getTime()));
-        response.setEndDate(endDate == null ? null : new 
Date(endDate.getTime()));
-
+        QuotaStatementResponse response = 
responseBuilder.createQuotaStatementResponse(this);
+        response.setStartDate(startDate);
+        response.setEndDate(endDate);
         response.setResponseName(getCommandName());
         setResponseObject(response);
     }
-
 }
diff --git 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
index 177fb00d4b5..bde905c487b 100644
--- 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
+++ 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
@@ -32,7 +32,6 @@ import 
org.apache.cloudstack.api.command.QuotaValidateActivationRuleCmd;
 import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
 import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
 import org.apache.cloudstack.quota.vo.QuotaTariffVO;
-import org.apache.cloudstack.quota.vo.QuotaUsageVO;
 
 import java.util.Date;
 import java.util.List;
@@ -49,7 +48,7 @@ public interface QuotaResponseBuilder {
 
     boolean isUserAllowedToSeeActivationRules(User user);
 
-    QuotaStatementResponse createQuotaStatementResponse(List<QuotaUsageVO> 
quotaUsage);
+    QuotaStatementResponse createQuotaStatementResponse(QuotaStatementCmd cmd);
 
     QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> 
quotaUsage, Date startDate, Date endDate);
 
@@ -57,8 +56,6 @@ public interface QuotaResponseBuilder {
 
     QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> 
quotaBalance, Date startDate);
 
-    List<QuotaUsageVO> getQuotaUsage(QuotaStatementCmd cmd);
-
     List<QuotaBalanceVO> getQuotaBalance(QuotaBalanceCmd cmd);
 
     QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double 
amount, Long updatedBy, Boolean enforce);
diff --git 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
index 2d6ec3255f4..173c0723731 100644
--- 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
+++ 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
@@ -21,13 +21,11 @@ import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
 import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.time.LocalDate;
 import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
@@ -36,6 +34,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -43,12 +42,36 @@ import java.util.stream.Collectors;
 import javax.inject.Inject;
 
 import com.cloud.domain.Domain;
+import com.cloud.domain.DomainVO;
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.event.ActionEvent;
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.PermissionDeniedException;
-import com.cloud.projects.dao.ProjectDao;
+import com.cloud.network.dao.IPAddressDao;
+import com.cloud.network.dao.IPAddressVO;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
+import com.cloud.offerings.dao.NetworkOfferingDao;
+import com.cloud.offerings.NetworkOfferingVO;
+import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.dao.SnapshotDao;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.SnapshotVO;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.AccountVO;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.user.dao.UserDao;
 import com.cloud.user.User;
 import com.cloud.user.UserVO;
 import com.cloud.utils.DateUtil;
+import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.ServerApiException;
@@ -94,7 +117,8 @@ import 
org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
 import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
 import org.apache.cloudstack.quota.vo.QuotaSummaryVO;
 import org.apache.cloudstack.quota.vo.QuotaTariffVO;
-import org.apache.cloudstack.quota.vo.QuotaUsageVO;
+import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
+import org.apache.cloudstack.quota.vo.QuotaUsageResourceVO;
 import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
 import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
 import org.apache.commons.collections4.CollectionUtils;
@@ -106,18 +130,6 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.springframework.stereotype.Component;
 
-import com.cloud.domain.DomainVO;
-import com.cloud.domain.dao.DomainDao;
-import com.cloud.event.ActionEvent;
-import com.cloud.event.EventTypes;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.user.Account;
-import com.cloud.user.AccountManager;
-import com.cloud.user.AccountVO;
-import com.cloud.user.dao.AccountDao;
-import com.cloud.user.dao.UserDao;
-import com.cloud.utils.Pair;
-
 @Component
 public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
     protected Logger logger = LogManager.getLogger(getClass());
@@ -140,8 +152,6 @@ public class QuotaResponseBuilderImpl implements 
QuotaResponseBuilder {
     @Inject
     private AccountDao _accountDao;
     @Inject
-    private ProjectDao projectDao;
-    @Inject
     private QuotaAccountDao quotaAccountDao;
     @Inject
     private DomainDao domainDao;
@@ -159,6 +169,21 @@ public class QuotaResponseBuilderImpl implements 
QuotaResponseBuilder {
     private JsInterpreterHelper jsInterpreterHelper;
     @Inject
     private ApiDiscoveryService apiDiscoveryService;
+    @Inject
+    private IPAddressDao ipAddressDao;
+    @Inject
+    private NetworkDao networkDao;
+    @Inject
+    private NetworkOfferingDao networkOfferingDao;
+    @Inject
+    private SnapshotDao snapshotDao;
+    @Inject
+    private VMInstanceDao vmInstanceDao;
+    @Inject
+    private VMTemplateDao vmTemplateDao;
+    @Inject
+    private VolumeDao volumeDao;
+
 
     private final Class<?>[] assignableClasses = {GenericPresetVariable.class, 
ComputingResources.class};
 
@@ -393,76 +418,210 @@ public class QuotaResponseBuilderImpl implements 
QuotaResponseBuilder {
     }
 
     @Override
-    public QuotaStatementResponse createQuotaStatementResponse(final 
List<QuotaUsageVO> quotaUsage) {
-        if (quotaUsage == null || quotaUsage.isEmpty()) {
-            throw new InvalidParameterValueException("There is no usage data 
found for period mentioned.");
-        }
+    public QuotaStatementResponse 
createQuotaStatementResponse(QuotaStatementCmd cmd) {
+        Long accountId = getAccountIdForQuotaStatement(cmd);
+        Long domainId = getDomainIdForQuotaStatement(cmd, accountId);
+        List<QuotaUsageJoinVO> quotaUsages = 
_quotaService.getQuotaUsage(accountId, null, domainId, cmd.getUsageType(), 
cmd.getStartDate(), cmd.getEndDate());
+
+        logger.debug("Creating quota statement from [{}] usage records for 
parameters [{}].", quotaUsages.size(),
+                ReflectionToStringBuilderUtils.reflectOnlySelectedFields(cmd, 
"accountName", "accountId", "projectId", "domainId", "startDate", "endDate", 
"usageType", "showResources"));
+        createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(quotaUsages, 
cmd.getUsageType());
+
+        Map<Integer, List<QuotaUsageJoinVO>> recordsPerUsageTypes = 
quotaUsages.stream()
+                
.sorted(Comparator.comparingInt(QuotaUsageJoinVO::getUsageType))
+                
.collect(Collectors.groupingBy(QuotaUsageJoinVO::getUsageType));
+
+        List<QuotaStatementItemResponse> items = new ArrayList<>();
+        recordsPerUsageTypes.forEach((key, value) -> 
items.add(createStatementItem(key, value, cmd.isShowResources())));
 
         QuotaStatementResponse statement = new QuotaStatementResponse();
+        statement.setLineItem(items);
+        
statement.setTotalQuota(items.stream().map(QuotaStatementItemResponse::getQuotaUsed).reduce(BigDecimal.ZERO,
 BigDecimal::add));
+        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
+        statement.setObjectName("statement");
 
-        HashMap<Integer, QuotaTypes> quotaTariffMap = new HashMap<Integer, 
QuotaTypes>();
-        Collection<QuotaTypes> result = QuotaTypes.listQuotaTypes().values();
+        if (accountId != null) {
+            Account account = _accountDao.findByIdIncludingRemoved(accountId);
+            statement.setAccountId(account.getUuid());
+            statement.setAccountName(account.getAccountName());
+            domainId = account.getDomainId();
+        }
+        if (domainId != null) {
+            DomainVO domain = domainDao.findByIdIncludingRemoved(domainId);
+            statement.setDomainId(domain.getUuid());
+        }
+
+        return statement;
+    }
 
-        for (QuotaTypes quotaTariff : result) {
-            quotaTariffMap.put(quotaTariff.getQuotaType(), quotaTariff);
-            // add dummy record for each usage type
-            QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
-            dummy.setUsageType(quotaTariff.getQuotaType());
-            dummy.setQuotaUsed(new BigDecimal(0));
-            quotaUsage.add(dummy);
+    protected Long getAccountIdForQuotaStatement(QuotaStatementCmd cmd) {
+        if 
(Account.Type.NORMAL.equals(CallContext.current().getCallingAccount().getType()))
 {
+            logger.debug("Limiting the Quota statement for the calling 
Account, as they are a User Account.");
+            return CallContext.current().getCallingAccountId();
         }
 
-        if (logger.isDebugEnabled()) {
-            logger.debug(
-                    "createQuotaStatementResponse Type=" + 
quotaUsage.get(0).getUsageType() + " usage=" + 
quotaUsage.get(0).getQuotaUsed().setScale(2, RoundingMode.HALF_EVEN)
-                    + " rec.id=" + quotaUsage.get(0).getUsageItemId() + " SD=" 
+ quotaUsage.get(0).getStartDate() + " ED=" + quotaUsage.get(0).getEndDate());
+        long accountId = cmd.getEntityOwnerId();
+        if (accountId != -1) {
+            return accountId;
         }
 
-        Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
-            @Override
-            public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
-                if (o1.getUsageType() == o2.getUsageType()) {
-                    return 0;
-                }
-                return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
-            }
-        });
+        if (cmd.getDomainId() == null) {
+            logger.debug("Limiting the Quota statement for the calling 
Account, as 'domainid' was not informed.");
+            return CallContext.current().getCallingAccountId();
+        }
+
+        logger.debug("Allowing admin/domain admin to generate the Quota 
statement for the provided Domain.");
+        return null;
+    }
+
+    protected Long getDomainIdForQuotaStatement(QuotaStatementCmd cmd, Long 
accountId) {
+        if (accountId != null) {
+            logger.debug("Quota statement is already limited to Account 
[{}].", accountId);
+            Account account = _accountDao.findByIdIncludingRemoved(accountId);
+            return account.getDomainId();
+        }
+
+        Long domainId = cmd.getDomainId();
+        if (domainId != null) {
+            return domainId;
+        }
+
+        logger.debug("Limiting the Quota statement for the calling Account's 
Domain.");
+        return CallContext.current().getCallingAccount().getDomainId();
+    }
+
+    protected void 
createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(List<QuotaUsageJoinVO>
 quotaUsages, Integer usageType) {
+        if (usageType != null) {
+            logger.debug("As the usage type [{}] was informed as parameter of 
the API quotaStatement, we will not create dummy records.", usageType);
+            return;
+
+        }
+
+        for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) {
+            QuotaUsageJoinVO dummy = new QuotaUsageJoinVO();
+            dummy.setUsageType(quotaType);
+            dummy.setQuotaUsed(BigDecimal.ZERO);
+            quotaUsages.add(dummy);
+        }
+    }
+
+    protected QuotaStatementItemResponse createStatementItem(int usageType, 
List<QuotaUsageJoinVO> usageRecords, boolean showResources) {
+        QuotaUsageJoinVO firstRecord = usageRecords.get(0);
+        int type = firstRecord.getUsageType();
+
+        QuotaTypes quotaType = QuotaTypes.listQuotaTypes().get(type);
+
+        QuotaStatementItemResponse item = new QuotaStatementItemResponse(type);
+        
item.setQuotaUsed(usageRecords.stream().map(QuotaUsageJoinVO::getQuotaUsed).filter(Objects::nonNull).reduce(BigDecimal.ZERO,
 BigDecimal::add));
+        item.setUsageUnit(quotaType.getQuotaUnit());
+        item.setUsageName(quotaType.getQuotaName());
+
+        setStatementItemResources(item, usageType, usageRecords, 
showResources);
+        return item;
+    }
+
+    protected void setStatementItemResources(QuotaStatementItemResponse 
statementItem, int usageType, List<QuotaUsageJoinVO> quotaUsageRecords, boolean 
showResources) {
+        if (!showResources) {
+            return;
+        }
+
+        List<QuotaStatementItemResourceResponse> itemDetails = new 
ArrayList<>();
+
+        Map<Long, BigDecimal> quotaUsagesValuesAggregatedById = 
quotaUsageRecords
+                .stream()
+                .filter(quotaUsageJoinVo -> 
getResourceIdByUsageType(quotaUsageJoinVo, usageType) != null)
+                .collect(Collectors.groupingBy(
+                        quotaUsageJoinVo -> 
getResourceIdByUsageType(quotaUsageJoinVo, usageType),
+                        Collectors.reducing(new BigDecimal(0), 
QuotaUsageJoinVO::getQuotaUsed, BigDecimal::add)
+                ));
+
+        for (Map.Entry<Long, BigDecimal> entry : 
quotaUsagesValuesAggregatedById.entrySet()) {
+            QuotaStatementItemResourceResponse detail = new 
QuotaStatementItemResourceResponse();
+
+            detail.setQuotaUsed(entry.getValue());
+
+            QuotaUsageResourceVO resource = 
getResourceFromIdAndType(entry.getKey(), usageType);
+            if (resource != null) {
+                detail.setResourceId(resource.getUuid());
+                detail.setDisplayName(resource.getName());
+                detail.setRemoved(resource.isRemoved());
+            } else {
+                detail.setDisplayName("<untraceable>");
 
-        List<QuotaStatementItemResponse> items = new 
ArrayList<QuotaStatementItemResponse>();
-        QuotaStatementItemResponse lineitem;
-        int type = -1;
-        BigDecimal usage = new BigDecimal(0);
-        BigDecimal totalUsage = new BigDecimal(0);
-        quotaUsage.add(new QuotaUsageVO());// boundary
-        QuotaUsageVO prev = quotaUsage.get(0);
-        if (logger.isDebugEnabled()) {
-            logger.debug("createQuotaStatementResponse record count=" + 
quotaUsage.size());
-        }
-        for (final QuotaUsageVO quotaRecord : quotaUsage) {
-            if (type != quotaRecord.getUsageType()) {
-                if (type != -1) {
-                    lineitem = new QuotaStatementItemResponse(type);
-                    lineitem.setQuotaUsed(usage);
-                    lineitem.setAccountId(prev.getAccountId());
-                    lineitem.setDomainId(prev.getDomainId());
-                    
lineitem.setUsageUnit(quotaTariffMap.get(type).getQuotaUnit());
-                    
lineitem.setUsageName(quotaTariffMap.get(type).getQuotaName());
-                    lineitem.setObjectName("quotausage");
-                    items.add(lineitem);
-                    totalUsage = totalUsage.add(usage);
-                    usage = new BigDecimal(0);
-                }
-                type = quotaRecord.getUsageType();
             }
-            prev = quotaRecord;
-            usage = usage.add(quotaRecord.getQuotaUsed());
+            itemDetails.add(detail);
         }
+        statementItem.setResources(itemDetails);
+    }
 
-        statement.setLineItem(items);
-        statement.setTotalQuota(totalUsage);
-        statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
-        statement.setObjectName("statement");
-        return statement;
+    protected Long getResourceIdByUsageType(QuotaUsageJoinVO quotaUsageJoinVo, 
int usageType) {
+        switch (usageType) {
+            case QuotaTypes.NETWORK_BYTES_SENT:
+            case QuotaTypes.NETWORK_BYTES_RECEIVED:
+                return quotaUsageJoinVo.getNetworkId();
+            case QuotaTypes.NETWORK_OFFERING:
+                return quotaUsageJoinVo.getOfferingId();
+            default:
+                return quotaUsageJoinVo.getResourceId();
+        }
+    }
+
+    protected QuotaUsageResourceVO getResourceFromIdAndType(long resourceId, 
int usageType) {
+        switch (usageType) {
+            case QuotaTypes.ALLOCATED_VM:
+            case QuotaTypes.RUNNING_VM:
+                VMInstanceVO vmInstance = 
vmInstanceDao.findByIdIncludingRemoved(resourceId);
+                if (vmInstance != null) {
+                    return new QuotaUsageResourceVO(vmInstance.getUuid(), 
vmInstance.getHostName(), vmInstance.getRemoved());
+                }
+                break;
+            case QuotaTypes.VOLUME:
+            case QuotaTypes.VOLUME_SECONDARY:
+            case QuotaTypes.VM_DISK_BYTES_READ:
+            case QuotaTypes.VM_DISK_BYTES_WRITE:
+            case QuotaTypes.VM_DISK_IO_READ:
+            case QuotaTypes.VM_DISK_IO_WRITE:
+                VolumeVO volume = 
volumeDao.findByIdIncludingRemoved(resourceId);
+                if (volume != null) {
+                    return new QuotaUsageResourceVO(volume.getUuid(), 
volume.getName(), volume.getRemoved());
+                }
+                break;
+            case QuotaTypes.VM_SNAPSHOT_ON_PRIMARY:
+            case QuotaTypes.VM_SNAPSHOT:
+            case QuotaTypes.SNAPSHOT:
+                SnapshotVO snapshot = 
snapshotDao.findByIdIncludingRemoved(resourceId);
+                if (snapshot != null) {
+                    return new QuotaUsageResourceVO(snapshot.getUuid(), 
snapshot.getName(), snapshot.getRemoved());
+                }
+                break;
+            case QuotaTypes.NETWORK_BYTES_SENT:
+            case QuotaTypes.NETWORK_BYTES_RECEIVED:
+                NetworkVO network = 
networkDao.findByIdIncludingRemoved(resourceId);
+                if (network != null) {
+                    return new QuotaUsageResourceVO(network.getUuid(), 
network.getName(), network.getRemoved());
+                }
+                break;
+            case QuotaTypes.TEMPLATE:
+            case QuotaTypes.ISO:
+                VMTemplateVO vmTemplate = 
vmTemplateDao.findByIdIncludingRemoved(resourceId);
+                if (vmTemplate != null) {
+                    return new QuotaUsageResourceVO(vmTemplate.getUuid(), 
vmTemplate.getName(), vmTemplate.getRemoved());
+                }
+                break;
+            case QuotaTypes.NETWORK_OFFERING:
+                NetworkOfferingVO networkOffering = 
networkOfferingDao.findByIdIncludingRemoved(resourceId);
+                if (networkOffering != null) {
+                    return new QuotaUsageResourceVO(networkOffering.getUuid(), 
networkOffering.getName(), networkOffering.getRemoved());
+                }
+                break;
+            case QuotaTypes.IP_ADDRESS:
+                IPAddressVO ipAddress = 
ipAddressDao.findByIdIncludingRemoved(resourceId);
+                if (ipAddress != null) {
+                    return new QuotaUsageResourceVO(ipAddress.getUuid(), 
ipAddress.getName(), ipAddress.getRemoved());
+                }
+                break;
+        }
+        return null;
     }
 
     @Override
@@ -518,14 +677,14 @@ public class QuotaResponseBuilderImpl implements 
QuotaResponseBuilder {
     }
 
     protected void warnQuotaTariffUpdateDeprecatedFields(QuotaTariffUpdateCmd 
cmd) {
-        String warnMessage = "The parameter 's%s' for API 'quotaTariffUpdate' 
is no longer needed and it will be removed in future releases.";
+        String warnMessage = "The parameter '{}' for API 'quotaTariffUpdate' 
is no longer needed and it will be removed in future releases.";
 
         if (cmd.getStartDate() != null) {
-            logger.warn(String.format(warnMessage,"startdate"));
+            logger.warn(warnMessage, "startdate");
         }
 
         if (cmd.getUsageType() != null) {
-            logger.warn(String.format(warnMessage,"usagetype"));
+            logger.warn(warnMessage, "usagetype");
         }
     }
 
@@ -712,11 +871,6 @@ public class QuotaResponseBuilderImpl implements 
QuotaResponseBuilder {
         return resp;
     }
 
-    @Override
-    public List<QuotaUsageVO> getQuotaUsage(QuotaStatementCmd cmd) {
-        return _quotaService.getQuotaUsage(cmd.getAccountId(), 
cmd.getAccountName(), cmd.getDomainId(), cmd.getUsageType(), 
cmd.getStartDate(), cmd.getEndDate());
-    }
-
     @Override
     public List<QuotaBalanceVO> getQuotaBalance(QuotaBalanceCmd cmd) {
         return _quotaService.findQuotaBalanceVO(cmd.getAccountId(), 
cmd.getAccountName(), cmd.getDomainId(), cmd.getStartDate(), cmd.getEndDate());
diff --git 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementItemResourceResponse.java
 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementItemResourceResponse.java
new file mode 100644
index 00000000000..3e052f73339
--- /dev/null
+++ 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementItemResourceResponse.java
@@ -0,0 +1,61 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.api.response;
+
+import java.math.BigDecimal;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+
+public class QuotaStatementItemResourceResponse extends BaseResponse {
+
+    @SerializedName(ApiConstants.QUOTA_CONSUMED)
+    @Param(description = "Quota consumed.")
+    private BigDecimal quotaUsed;
+
+    @SerializedName(ApiConstants.RESOURCE_ID)
+    @Param(description = "Resources's ID.")
+    private String resourceId;
+
+    @SerializedName(ApiConstants.DISPLAY_NAME)
+    @Param(description = "Resource's display name.")
+    private String displayName;
+
+    @SerializedName(ApiConstants.REMOVED)
+    @Param(description = "Indicates whether the resource is removed or 
active.")
+    private boolean removed;
+
+    public void setQuotaUsed(BigDecimal quotaUsed) {
+        this.quotaUsed = quotaUsed;
+    }
+
+    public void setResourceId(String resourceId) {
+        this.resourceId = resourceId;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public void setRemoved(boolean removed) {
+        this.removed = removed;
+    }
+}
diff --git 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementItemResponse.java
 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementItemResponse.java
index c370d82b3cc..0747c5a9172 100644
--- 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementItemResponse.java
+++ 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementItemResponse.java
@@ -17,72 +17,41 @@
 package org.apache.cloudstack.api.response;
 
 import java.math.BigDecimal;
-import java.math.RoundingMode;
+import java.util.List;
 
 import com.google.gson.annotations.SerializedName;
 
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseResponse;
 
 import com.cloud.serializer.Param;
 
 public class QuotaStatementItemResponse extends BaseResponse {
 
-    @SerializedName("type")
-    @Param(description = "Usage type")
+    @SerializedName(ApiConstants.TYPE)
+    @Param(description = "Usage type.")
     private int usageType;
 
-    @SerializedName("accountid")
-    @Param(description = "Account id")
-    private Long accountId;
-
-    @SerializedName("account")
-    @Param(description = "Account name")
-    private String accountName;
-
-    @SerializedName("domain")
-    @Param(description = "Domain id")
-    private Long domainId;
-
-    @SerializedName("name")
-    @Param(description = "Usage type name")
+    @SerializedName(ApiConstants.NAME)
+    @Param(description = "Name of the Usage type.")
     private String usageName;
 
-    @SerializedName("unit")
-    @Param(description = "Usage unit")
+    @SerializedName(ApiConstants.UNIT)
+    @Param(description = "Unit of the Usage type.")
     private String usageUnit;
 
-    @SerializedName("quota")
-    @Param(description = "Quota consumed")
+    @SerializedName(ApiConstants.QUOTA)
+    @Param(description = "Quota consumed.")
     private BigDecimal quotaUsed;
 
+    @SerializedName(ApiConstants.RESOURCES)
+    @Param(description = "Item's resources.")
+    private List<QuotaStatementItemResourceResponse> resources;
+
     public QuotaStatementItemResponse(final int usageType) {
         this.usageType = usageType;
     }
 
-    public Long getAccountId() {
-        return accountId;
-    }
-
-    public void setAccountId(Long accountId) {
-        this.accountId = accountId;
-    }
-
-    public String getAccountName() {
-        return accountName;
-    }
-
-    public void setAccountName(String accountName) {
-        this.accountName = accountName;
-    }
-
-    public Long getDomainId() {
-        return domainId;
-    }
-
-    public void setDomainId(Long domainId) {
-        this.domainId = domainId;
-    }
-
     public String getUsageName() {
         return usageName;
     }
@@ -112,7 +81,15 @@ public class QuotaStatementItemResponse extends 
BaseResponse {
     }
 
     public void setQuotaUsed(BigDecimal quotaUsed) {
-        this.quotaUsed = quotaUsed.setScale(2, RoundingMode.HALF_EVEN);
+        this.quotaUsed = quotaUsed;
+    }
+
+    public List<QuotaStatementItemResourceResponse> getResources() {
+        return resources;
+    }
+
+    public void setResources(List<QuotaStatementItemResourceResponse> 
resources) {
+        this.resources = resources;
     }
 
 }
diff --git 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementResponse.java
 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementResponse.java
index 0a7ba4dbeb9..81cb1011182 100644
--- 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementResponse.java
+++ 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaStatementResponse.java
@@ -18,56 +18,56 @@ package org.apache.cloudstack.api.response;
 
 import com.cloud.serializer.Param;
 import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseResponse;
 
 import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.util.Date;
 import java.util.List;
 
 public class QuotaStatementResponse  extends BaseResponse {
 
-    @SerializedName("accountid")
-    @Param(description = "Account ID")
-    private Long accountId;
+    @SerializedName(ApiConstants.ACCOUNT_ID)
+    @Param(description = "ID of the Account.")
+    private String accountId;
 
-    @SerializedName("account")
-    @Param(description = "Account name")
+    @SerializedName(ApiConstants.ACCOUNT)
+    @Param(description = "Name of the Account.")
     private String accountName;
 
-    @SerializedName("domain")
-    @Param(description = "Domain ID")
-    private Long domainId;
+    @SerializedName(ApiConstants.DOMAIN)
+    @Param(description = "ID of the Domain.")
+    private String domainId;
 
-    @SerializedName("quotausage")
-    @Param(description = "List of quota usage under various types", 
responseObject = QuotaStatementItemResponse.class)
+    @SerializedName(ApiConstants.QUOTA_USAGE)
+    @Param(description = "List of Quota usage under various types.", 
responseObject = QuotaStatementItemResponse.class)
     private List<QuotaStatementItemResponse> lineItem;
 
-    @SerializedName("totalquota")
-    @Param(description = "Total quota used during this period")
+    @SerializedName(ApiConstants.TOTAL_QUOTA)
+    @Param(description = "Total Quota consumed during this period.")
     private BigDecimal totalQuota;
 
-    @SerializedName("startdate")
-    @Param(description = "Start date")
+    @SerializedName(ApiConstants.START_DATE)
+    @Param(description = "Start date of the Quota statement.")
     private Date startDate = null;
 
-    @SerializedName("enddate")
-    @Param(description = "End date")
+    @SerializedName(ApiConstants.END_DATE)
+    @Param(description = "End date of the Quota statement.")
     private Date endDate = null;
 
-    @SerializedName("currency")
-    @Param(description = "Currency")
+    @SerializedName(ApiConstants.CURRENCY)
+    @Param(description = "Currency of the Quota statement.")
     private String currency;
 
     public QuotaStatementResponse() {
         super();
     }
 
-    public Long getAccountId() {
+    public String getAccountId() {
         return accountId;
     }
 
-    public void setAccountId(Long accountId) {
+    public void setAccountId(String accountId) {
         this.accountId = accountId;
     }
 
@@ -79,45 +79,36 @@ public class QuotaStatementResponse  extends BaseResponse {
         this.accountName = accountName;
     }
 
-    public Long getDomainId() {
+    public String getDomainId() {
         return domainId;
     }
 
-    public void setDomainId(Long domainId) {
+    public void setDomainId(String domainId) {
         this.domainId = domainId;
     }
 
-    public List<QuotaStatementItemResponse> getLineItem() {
-        return lineItem;
-    }
-
     public void setLineItem(List<QuotaStatementItemResponse> lineItem) {
         this.lineItem = lineItem;
     }
 
     public Date getStartDate() {
-        return startDate == null ? null : new Date(startDate.getTime());
+        return startDate;
     }
 
     public void setStartDate(Date startDate) {
-        this.startDate = startDate == null ? null : new 
Date(startDate.getTime());
+        this.startDate = startDate;
     }
 
     public Date getEndDate() {
-        return endDate == null ? null : new Date(endDate.getTime());
+        return endDate;
     }
 
     public void setEndDate(Date endDate) {
-        this.endDate = endDate == null ? null : new Date(endDate.getTime());
-    }
-
-
-    public BigDecimal getTotalQuota() {
-        return totalQuota;
+        this.endDate = endDate;
     }
 
     public void setTotalQuota(BigDecimal totalQuota) {
-        this.totalQuota = totalQuota.setScale(2, RoundingMode.HALF_EVEN);
+        this.totalQuota = totalQuota;
     }
 
     public String getCurrency() {
diff --git 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaService.java
 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaService.java
index a421d0f066a..78acfc11682 100644
--- 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaService.java
+++ 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaService.java
@@ -21,14 +21,14 @@ import java.util.Date;
 import java.util.List;
 
 import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
-import org.apache.cloudstack.quota.vo.QuotaUsageVO;
+import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
 
 import com.cloud.user.AccountVO;
 import com.cloud.utils.component.PluggableService;
 
 public interface QuotaService extends PluggableService {
 
-    List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long 
domainId, Integer usageType, Date startDate, Date endDate);
+    List<QuotaUsageJoinVO> getQuotaUsage(Long accountId, String accountName, 
Long domainId, Integer usageType, Date startDate, Date endDate);
 
     List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String 
accountName, Long domainId, Date startDate, Date endDate);
 
diff --git 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
index 2d7d623d1d9..a0ba2fbc751 100644
--- 
a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
+++ 
b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
@@ -53,10 +53,10 @@ import 
org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.quota.constant.QuotaConfig;
 import org.apache.cloudstack.quota.dao.QuotaAccountDao;
 import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
-import org.apache.cloudstack.quota.dao.QuotaUsageDao;
+import org.apache.cloudstack.quota.dao.QuotaUsageJoinDao;
 import org.apache.cloudstack.quota.vo.QuotaAccountVO;
 import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
-import org.apache.cloudstack.quota.vo.QuotaUsageVO;
+import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
 import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.stereotype.Component;
 
@@ -80,7 +80,7 @@ public class QuotaServiceImpl extends ManagerBase implements 
QuotaService, Confi
     @Inject
     private QuotaAccountDao _quotaAcc;
     @Inject
-    private QuotaUsageDao _quotaUsageDao;
+    private QuotaUsageJoinDao quotaUsageJoinDao;
     @Inject
     private DomainDao _domainDao;
     @Inject
@@ -213,27 +213,7 @@ public class QuotaServiceImpl extends ManagerBase 
implements QuotaService, Confi
     }
 
     @Override
-    public List<QuotaUsageVO> getQuotaUsage(Long accountId, String 
accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
-        // if accountId is not specified, use accountName and domainId
-        if ((accountId == null) && (accountName != null) && (domainId != 
null)) {
-            Account userAccount = null;
-            Account caller = CallContext.current().getCallingAccount();
-            if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
-                Filter filter = new Filter(AccountVO.class, "id", 
Boolean.FALSE, null, null);
-                List<AccountVO> accounts = 
_accountDao.listAccounts(accountName, domainId, filter);
-                if (!accounts.isEmpty()) {
-                    userAccount = accounts.get(0);
-                }
-                if (userAccount != null) {
-                    accountId = userAccount.getId();
-                } else {
-                    throw new InvalidParameterValueException("Unable to find 
account " + accountName + " in domain " + domainId);
-                }
-            } else {
-                throw new PermissionDeniedException("Invalid Domain Id or 
Account");
-            }
-        }
-
+    public List<QuotaUsageJoinVO> getQuotaUsage(Long accountId, String 
accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
         if (startDate.after(endDate)) {
             throw new InvalidParameterValueException("Incorrect Date Range. 
Start date: " + startDate + " is after end date:" + endDate);
         }
@@ -241,7 +221,7 @@ public class QuotaServiceImpl extends ManagerBase 
implements QuotaService, Confi
         logger.debug("Getting quota records of type [{}] for account [{}] in 
domain [{}], between [{}] and [{}].",
                 usageType, accountId, domainId, startDate, endDate);
 
-        return _quotaUsageDao.findQuotaUsage(accountId, domainId, usageType, 
startDate, endDate);
+        return quotaUsageJoinDao.findQuotaUsage(accountId, domainId, 
usageType, null, null, null, startDate, endDate, null);
     }
 
     @Override
diff --git 
a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaStatementCmdTest.java
 
b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaStatementCmdTest.java
index d6f9f747fa8..e67638db632 100644
--- 
a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaStatementCmdTest.java
+++ 
b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaStatementCmdTest.java
@@ -16,38 +16,29 @@
 // under the License.
 package org.apache.cloudstack.api.command;
 
-import junit.framework.TestCase;
 import org.apache.cloudstack.api.response.QuotaResponseBuilder;
 import org.apache.cloudstack.api.response.QuotaStatementResponse;
-import org.apache.cloudstack.quota.vo.QuotaUsageVO;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-
 @RunWith(MockitoJUnitRunner.class)
-public class QuotaStatementCmdTest extends TestCase {
+public class QuotaStatementCmdTest {
     @Mock
-    QuotaResponseBuilder responseBuilder;
+    QuotaResponseBuilder responseBuilderMock;
 
     @Test
-    public void testQuotaStatementCmd() throws NoSuchFieldException, 
IllegalAccessException {
+    public void executeTestVerifyCalls() {
         QuotaStatementCmd cmd = new QuotaStatementCmd();
         cmd.setAccountName("admin");
+        cmd.responseBuilder = responseBuilderMock;
 
-        Field rbField = 
QuotaStatementCmd.class.getDeclaredField("_responseBuilder");
-        rbField.setAccessible(true);
-        rbField.set(cmd, responseBuilder);
+        Mockito.doReturn(new 
QuotaStatementResponse()).when(responseBuilderMock).createQuotaStatementResponse(Mockito.any());
 
-        List<QuotaUsageVO> quotaUsageVOList = new ArrayList<QuotaUsageVO>();
-        
Mockito.when(responseBuilder.getQuotaUsage(Mockito.eq(cmd))).thenReturn(quotaUsageVOList);
-        
Mockito.when(responseBuilder.createQuotaStatementResponse(Mockito.eq(quotaUsageVOList))).thenReturn(new
 QuotaStatementResponse());
         cmd.execute();
-        Mockito.verify(responseBuilder, 
Mockito.times(1)).getQuotaUsage(Mockito.eq(cmd));
+
+        Mockito.verify(responseBuilderMock).createQuotaStatementResponse(cmd);
     }
 }
diff --git 
a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
 
b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
index ea88a106b84..81b4992082d 100644
--- 
a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
+++ 
b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
@@ -17,6 +17,8 @@
 package org.apache.cloudstack.api.response;
 
 import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -42,6 +44,7 @@ import 
org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
 import org.apache.cloudstack.api.command.QuotaCreditsListCmd;
 import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
 import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
+import org.apache.cloudstack.api.command.QuotaStatementCmd;
 import org.apache.cloudstack.api.command.QuotaSummaryCmd;
 import org.apache.cloudstack.api.command.QuotaValidateActivationRuleCmd;
 import org.apache.cloudstack.context.CallContext;
@@ -67,6 +70,7 @@ import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
 import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
 import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
 import org.apache.cloudstack.quota.vo.QuotaTariffVO;
+import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
 import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
 
 import org.apache.commons.lang3.time.DateUtils;
@@ -914,4 +918,199 @@ public class QuotaResponseBuilderImplTest extends 
TestCase {
         Assert.assertTrue(formattedVariables.containsValue("accountname"));
         Assert.assertTrue(formattedVariables.containsValue("zonename"));
     }
+
+    @Test
+    public void 
createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformedTestUsageTypeDifferentFromNullDoNothing()
 {
+        List<QuotaUsageJoinVO> listUsage = new ArrayList<>();
+
+        
quotaResponseBuilderSpy.createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(listUsage,
 1);
+
+        Assert.assertTrue(listUsage.isEmpty());
+    }
+
+    @Test
+    public void 
createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformedTestUsageTypeIsNullAddDummyForAllQuotaTypes()
 {
+        List<QuotaUsageJoinVO> listUsage = new ArrayList<>();
+        listUsage.add(new QuotaUsageJoinVO());
+
+        
quotaResponseBuilderSpy.createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(listUsage,
 null);
+
+        Assert.assertEquals(QuotaTypes.listQuotaTypes().size() + 1, 
listUsage.size());
+
+        QuotaTypes.listQuotaTypes().entrySet().forEach(entry -> {
+            Assert.assertTrue(listUsage.stream().anyMatch(usage -> 
usage.getUsageType() == entry.getKey() && 
usage.getQuotaUsed().equals(BigDecimal.ZERO)));
+        });
+    }
+
+    private List<QuotaUsageJoinVO> getQuotaUsagesForTest() {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+        List<QuotaUsageJoinVO> quotaUsages = new ArrayList<>();
+
+        QuotaUsageJoinVO quotaUsage = new QuotaUsageJoinVO();
+        quotaUsage.setAccountId(1l);
+        quotaUsage.setDomainId(2l);
+        quotaUsage.setUsageType(3);
+        quotaUsage.setQuotaUsed(BigDecimal.valueOf(10));
+        try {
+            quotaUsage.setStartDate(sdf.parse("2022-01-01"));
+            quotaUsage.setEndDate(sdf.parse("2022-01-02"));
+        } catch (ParseException ignored) {
+        }
+        quotaUsages.add(quotaUsage);
+
+        quotaUsage = new QuotaUsageJoinVO();
+        quotaUsage.setAccountId(4l);
+        quotaUsage.setDomainId(5l);
+        quotaUsage.setUsageType(3);
+        quotaUsage.setQuotaUsed(null);
+        try {
+            quotaUsage.setStartDate(sdf.parse("2022-01-03"));
+            quotaUsage.setEndDate(sdf.parse("2022-01-04"));
+        } catch (ParseException ignored) {
+        }
+        quotaUsages.add(quotaUsage);
+
+        quotaUsage = new QuotaUsageJoinVO();
+        quotaUsage.setAccountId(6l);
+        quotaUsage.setDomainId(7l);
+        quotaUsage.setUsageType(3);
+        quotaUsage.setQuotaUsed(BigDecimal.valueOf(5));
+        try {
+            quotaUsage.setStartDate(sdf.parse("2022-01-05"));
+            quotaUsage.setEndDate(sdf.parse("2022-01-06"));
+        } catch (ParseException ignored) {
+        }
+        quotaUsages.add(quotaUsage);
+
+        return quotaUsages;
+    }
+
+    @Test
+    public void createStatementItemTestReturnItem() {
+        List<QuotaUsageJoinVO> quotaUsages = getQuotaUsagesForTest();
+        
Mockito.doNothing().when(quotaResponseBuilderSpy).setStatementItemResources(Mockito.any(),
 Mockito.anyInt(), Mockito.any(), Mockito.anyBoolean());
+
+        QuotaStatementItemResponse result = 
quotaResponseBuilderSpy.createStatementItem(0, quotaUsages, false);
+
+        QuotaUsageJoinVO expected = quotaUsages.get(0);
+        QuotaTypes quotaTypeExpected = 
QuotaTypes.listQuotaTypes().get(expected.getUsageType());
+        Assert.assertEquals(BigDecimal.valueOf(15), result.getQuotaUsed());
+        Assert.assertEquals(quotaTypeExpected.getQuotaUnit(), 
result.getUsageUnit());
+        Assert.assertEquals(quotaTypeExpected.getQuotaName(), 
result.getUsageName());
+    }
+
+    @Test
+    public void setStatementItemResourcesTestDoNotShowResourcesDoNothing() {
+        QuotaStatementItemResponse item = new QuotaStatementItemResponse(1);
+
+        quotaResponseBuilderSpy.setStatementItemResources(item, 0, 
getQuotaUsagesForTest(), false);
+
+        Assert.assertNull(item.getResources());
+    }
+
+    @Test
+    public void 
getAccountIdForQuotaStatementTestLimitsToCallingAccountForNormalUser() {
+        QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
+
+        
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
+        Mockito.doReturn(Account.Type.NORMAL).when(accountMock).getType();
+
+        try (MockedStatic<CallContext> callContextMocked = 
Mockito.mockStatic(CallContext.class)) {
+            
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
+
+            Long result = 
quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
+
+            
Assert.assertEquals(Long.valueOf(callerAccountMock.getAccountId()), result);
+        }
+    }
+
+    @Test
+    public void 
getAccountIdForQuotaStatementTestReturnsEntityOwnerIdWhenProvided() {
+        QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
+
+        Mockito.doReturn(42L).when(cmd).getEntityOwnerId();
+
+        Long result = 
quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
+
+        Assert.assertEquals(Long.valueOf(42L), result);
+    }
+
+    @Test
+    public void 
getAccountIdForQuotaStatementTestLimitsToCallingAccountWhenCallerIsAdminAndDomainIsNotProvided()
 {
+        QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
+
+        
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
+        Mockito.doReturn(Account.Type.ADMIN).when(accountMock).getType();
+        Mockito.doReturn(-1L).when(cmd).getEntityOwnerId();
+        Mockito.doReturn(null).when(cmd).getDomainId();
+
+        try (MockedStatic<CallContext> callContextMocked = 
Mockito.mockStatic(CallContext.class)) {
+            
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
+
+            Long result = 
quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
+
+            
Assert.assertEquals(Long.valueOf(callerAccountMock.getAccountId()), result);
+        }
+    }
+
+    @Test
+    public void 
getAccountIdForQuotaStatementTestReturnsNullWhenCallerIsAdminAndDomainIsProvided()
 {
+        QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
+
+        
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
+        Mockito.doReturn(Account.Type.ADMIN).when(accountMock).getType();
+        Mockito.doReturn(-1L).when(cmd).getEntityOwnerId();
+        Mockito.doReturn(10L).when(cmd).getDomainId();
+
+        try (MockedStatic<CallContext> callContextMocked = 
Mockito.mockStatic(CallContext.class)) {
+            
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
+
+            Long result = 
quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
+
+            Assert.assertNull(result);
+        }
+    }
+
+    @Test
+    public void 
getDomainIdForQuotaStatementTestReturnsAccountDomainIdWhenAccountIdIsProvided() 
{
+        QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
+        AccountVO account = Mockito.mock(AccountVO.class);
+
+        
Mockito.doReturn(account).when(accountDaoMock).findByIdIncludingRemoved(55L);
+        Mockito.doReturn(77L).when(account).getDomainId();
+
+        Long result = 
quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, 55L);
+
+        Assert.assertEquals(Long.valueOf(77L), result);
+    }
+
+    @Test
+    public void 
getDomainIdForQuotaStatementTestReturnsProvidedDomainIdWhenAccountIdIsNull() {
+        QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
+
+        Mockito.doReturn(99L).when(cmd).getDomainId();
+
+        Long result = 
quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, null);
+
+        Assert.assertEquals(Long.valueOf(99L), result);
+    }
+
+    @Test
+    public void 
getDomainIdForQuotaStatementTestFallsBackToCallingAccountDomainIdWhenNeitherAccountNorDomainIsProvided()
 {
+        QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
+        Account account = Mockito.mock(Account.class);
+
+        Mockito.doReturn(null).when(cmd).getDomainId();
+        Mockito.doReturn(123L).when(account).getDomainId();
+        Mockito.doReturn(account).when(callContextMock).getCallingAccount();
+
+        try (MockedStatic<CallContext> callContextMocked = 
Mockito.mockStatic(CallContext.class)) {
+            
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
+
+            Long result = 
quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, null);
+
+            Assert.assertEquals(123L, result.longValue());
+        }
+    }
 }
diff --git 
a/plugins/database/quota/src/test/java/org/apache/cloudstack/quota/QuotaServiceImplTest.java
 
b/plugins/database/quota/src/test/java/org/apache/cloudstack/quota/QuotaServiceImplTest.java
index 259264f3b0e..a0fe63de851 100644
--- 
a/plugins/database/quota/src/test/java/org/apache/cloudstack/quota/QuotaServiceImplTest.java
+++ 
b/plugins/database/quota/src/test/java/org/apache/cloudstack/quota/QuotaServiceImplTest.java
@@ -28,6 +28,7 @@ import org.apache.cloudstack.quota.constant.QuotaTypes;
 import org.apache.cloudstack.quota.dao.QuotaAccountDao;
 import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
 import org.apache.cloudstack.quota.dao.QuotaUsageDao;
+import org.apache.cloudstack.quota.dao.QuotaUsageJoinDao;
 import org.apache.cloudstack.quota.vo.QuotaAccountVO;
 import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
 import org.joda.time.DateTime;
@@ -63,6 +64,8 @@ public class QuotaServiceImplTest extends TestCase {
     @Mock
     QuotaBalanceDao quotaBalanceDao;
     @Mock
+    QuotaUsageJoinDao quotaUsageJoinDaoMock;
+    @Mock
     QuotaResponseBuilder respBldr;
     @Mock
     private AccountVO accountVoMock;
@@ -85,9 +88,9 @@ public class QuotaServiceImplTest extends TestCase {
         quotaAccountDaoField.setAccessible(true);
         quotaAccountDaoField.set(quotaServiceImplSpy, quotaAcc);
 
-        Field quotaUsageDaoField = 
QuotaServiceImpl.class.getDeclaredField("_quotaUsageDao");
+        Field quotaUsageDaoField = 
QuotaServiceImpl.class.getDeclaredField("quotaUsageJoinDao");
         quotaUsageDaoField.setAccessible(true);
-        quotaUsageDaoField.set(quotaServiceImplSpy, quotaUsageDao);
+        quotaUsageDaoField.set(quotaServiceImplSpy, quotaUsageJoinDaoMock);
 
         Field domainDaoField = 
QuotaServiceImpl.class.getDeclaredField("_domainDao");
         domainDaoField.setAccessible(true);
@@ -142,7 +145,8 @@ public class QuotaServiceImplTest extends TestCase {
         final Date endDate = new Date();
 
         quotaServiceImplSpy.getQuotaUsage(accountId, accountName, domainId, 
QuotaTypes.IP_ADDRESS, startDate, endDate);
-        Mockito.verify(quotaUsageDao, 
Mockito.times(1)).findQuotaUsage(Mockito.eq(accountId), Mockito.eq(domainId), 
Mockito.eq(QuotaTypes.IP_ADDRESS), Mockito.any(Date.class), 
Mockito.any(Date.class));
+        Mockito.verify(quotaUsageJoinDaoMock, 
Mockito.times(1)).findQuotaUsage(Mockito.eq(accountId), Mockito.eq(domainId), 
Mockito.eq(QuotaTypes.IP_ADDRESS), Mockito.any(),
+                Mockito.any(), Mockito.any(), Mockito.any(Date.class), 
Mockito.any(Date.class), Mockito.any());
     }
 
     @Test


Reply via email to