diff --git a/attendance/forms.py b/attendance/forms.py
index b960447f3..d7f3e363d 100644
--- a/attendance/forms.py
+++ b/attendance/forms.py
@@ -568,6 +568,7 @@ class NewRequestForm(AttendanceRequestForm):
excluded_fields = [
"id",
+ "attendance_id__employee_id",
"requested_data",
"at_work_second",
"approved_overtime_second",
@@ -615,7 +616,7 @@ class LateComeEarlyOutExportForm(forms.Form):
]
model_fields_2 = Attendance._meta.get_fields()
field_choices_2 = [
- (field.name, field.verbose_name)
+ ("attendance_id__" + field.name, field.verbose_name)
for field in model_fields_2
if hasattr(field, "verbose_name") and field.name not in excluded_fields
]
@@ -627,7 +628,11 @@ class LateComeEarlyOutExportForm(forms.Form):
initial=[
"employee_id",
"type",
- "is_active",
+ "attendance_id__attendance_date",
+ "attendance_id__attendance_clock_in_date",
+ "attendance_id__attendance_clock_in",
+ "attendance_id__attendance_clock_out_date",
+ "attendance_id__attendance_clock_out",
],
)
diff --git a/attendance/static/attendance/actions.js b/attendance/static/attendance/actions.js
index 68d59a1d4..e17135e34 100644
--- a/attendance/static/attendance/actions.js
+++ b/attendance/static/attendance/actions.js
@@ -402,6 +402,40 @@ function addingActivityIds() {
});
}
+function addinglatecomeIds() {
+ var ids = JSON.parse($("#selectedLatecome").attr("data-ids") || "[]");
+ var selectedCount = 0;
+
+ $(".all-latecome-row").each(function () {
+ if ($(this).is(":checked")) {
+ ids.push(this.id);
+ } else {
+ var index = ids.indexOf(this.id);
+ if (index > -1) {
+ ids.splice(index, 1);
+ }
+ }
+ });
+
+ ids = makelatecomeListUnique(ids);
+ selectedCount = ids.length;
+
+ getCurrentLanguageCode(function (code) {
+ languageCode = code;
+ var message = rowMessages[languageCode];
+
+ $("#selectedLatecome").attr("data-ids", JSON.stringify(ids));
+
+ if (selectedCount === 0) {
+ $("#selectedShowLatecome").css("display", "none");
+ $("#exportLatecome").css("display", "none");
+ } else {
+ $("#exportLatecome").css("display", "inline-flex");
+ $("#selectedShowLatecome").css("display", "inline-flex");
+ $("#selectedShowLatecome").text(selectedCount + " - " + message);
+ }
+ });
+}
function selectAllActivity() {
$("#selectedActivity").attr("data-clicked", 1);
$("#selectedShowActivity").removeAttr("style");
@@ -554,6 +588,125 @@ $("#attendance-info-import").click(function (e) {
});
});
+$(".all-latecome-row").change(function () {
+ addinglatecomeIds();
+});
+
+$(".all-latecome").change(function () {
+ setTimeout(() => {
+ addinglatecomeIds();
+ }, 100);
+});
+
+$("#selectAllLatecome").click(function () {
+ $("#selectedLatecome").attr("data-clicked", 1);
+ $("#selectedShowLatecome").removeAttr("style");
+ var savedFilters = JSON.parse(localStorage.getItem("savedFilters"));
+
+ if (savedFilters && savedFilters["filterData"] !== null) {
+ var filter = savedFilters["filterData"];
+
+ $.ajax({
+ url: "/attendance/latecome-attendance-select-filter",
+ data: { page: "all", filter: JSON.stringify(filter) },
+ type: "GET",
+ dataType: "json",
+ success: function (response) {
+ var employeeIds = response.employee_ids;
+
+ if (Array.isArray(employeeIds)) {
+ // Continue
+ } else {
+ console.error("employee_ids is not an array:", employeeIds);
+ }
+
+ var selectedCount = employeeIds.length;
+
+ for (var i = 0; i < employeeIds.length; i++) {
+ var empId = employeeIds[i];
+ $("#" + empId).prop("checked", true);
+ }
+ $("#selectedLatecome").attr("data-ids", JSON.stringify(employeeIds));
+
+ count = makelatecomeListUnique(employeeIds);
+ ticklatecomeCheckboxes(count);
+ },
+ error: function (xhr, status, error) {
+ console.error("Error:", error);
+ },
+ });
+ } else {
+ $.ajax({
+ url: "/attendance/latecome-attendance-select",
+ data: { page: "all" },
+ type: "GET",
+ dataType: "json",
+ success: function (response) {
+ var employeeIds = response.employee_ids;
+
+ if (Array.isArray(employeeIds)) {
+ // Continue
+ } else {
+ console.error("employee_ids is not an array:", employeeIds);
+ }
+
+ var selectedCount = employeeIds.length;
+
+ for (var i = 0; i < employeeIds.length; i++) {
+ var empId = employeeIds[i];
+ $("#" + empId).prop("checked", true);
+ }
+ var previousIds = $("#selectedLatecome").attr("data-ids");
+ $("#selectedLatecome").attr(
+ "data-ids",
+ JSON.stringify(
+ Array.from(new Set([...employeeIds, ...JSON.parse(previousIds)]))
+ )
+ );
+
+ count = makelatecomeListUnique(employeeIds);
+ ticklatecomeCheckboxes(count);
+ },
+ error: function (xhr, status, error) {
+ console.error("Error:", error);
+ },
+ });
+ }
+});
+
+$("#unselectAllLatecome").click(function () {
+ $("#selectedLatecome").attr("data-clicked", 0);
+
+ $.ajax({
+ url: "/attendance/latecome-attendance-select",
+ data: { page: "all", filter: "{}" },
+ type: "GET",
+ dataType: "json",
+ success: function (response) {
+ var employeeIds = response.employee_ids;
+
+ if (Array.isArray(employeeIds)) {
+ // Continue
+ } else {
+ console.error("employee_ids is not an array:", employeeIds);
+ }
+
+ for (var i = 0; i < employeeIds.length; i++) {
+ var empId = employeeIds[i];
+ $("#" + empId).prop("checked", false);
+ $(".all-latecome").prop("checked", false);
+ }
+ $("#selectedLatecome").attr("data-ids", JSON.stringify([]));
+
+ count = [];
+ ticklatecomeCheckboxes(count);
+ },
+ error: function (xhr, status, error) {
+ console.error("Error:", error);
+ },
+ });
+});
+
$("#select-all-fields").change(function () {
const isChecked = $(this).prop("checked");
$('[name="selected_fields"]').prop("checked", isChecked);
@@ -561,7 +714,13 @@ $("#select-all-fields").change(function () {
$(".all-latecome").change(function () {
const isLateChecked = $(this).prop("checked");
- $(".all-latecome-row").prop("checked", isLateChecked);
+ var closest = $(this)
+ .closest(".oh-sticky-table__thead")
+ .siblings(".oh-sticky-table__tbody");
+ $(closest)
+ .children()
+ .find(".all-latecome-row")
+ .prop("checked", isLateChecked);
});
$(".all-attendance-activity").change(function () {
@@ -718,6 +877,8 @@ $("#approveOt").click(function (e) {
});
});
+// -------------------------------------------Data Export Handlers---------------------------------------------------------------
+
$("#exportAccounts").click(function (e) {
var currentDate = new Date().toISOString().slice(0, 10);
var languageCode = null;
@@ -766,6 +927,106 @@ $("#exportAccounts").click(function (e) {
});
});
+$("#exportActivity").click(function (e) {
+ var currentDate = new Date().toISOString().slice(0, 10);
+ var languageCode = null;
+ ids = [];
+ ids.push($("#selectedActivity").attr("data-ids"));
+ ids = JSON.parse($("#selectedActivity").attr("data-ids"));
+ getCurrentLanguageCode(function (code) {
+ languageCode = code;
+ var confirmMessage = excelMessages[languageCode];
+ Swal.fire({
+ text: confirmMessage,
+ icon: "question",
+ showCancelButton: true,
+ confirmButtonColor: "#008000",
+ cancelButtonColor: "#d33",
+ confirmButtonText: "Confirm",
+ }).then(function (result) {
+ if (result.isConfirmed) {
+ $.ajax({
+ type: "GET",
+ url: "/attendance/attendance-activity-info-export",
+ data: {
+ ids: JSON.stringify(ids),
+ },
+ dataType: "binary",
+ xhrFields: {
+ responseType: "blob",
+ },
+ success: function (response) {
+ const file = new Blob([response], {
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ });
+ const url = URL.createObjectURL(file);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = "Attendance_activity" + currentDate + ".xlsx";
+ document.body.appendChild(link);
+ link.click();
+ },
+ error: function (xhr, textStatus, errorThrown) {
+ console.error("Error downloading file:", errorThrown);
+ },
+ });
+ }
+ });
+ });
+});
+
+$("#exportLatecome").click(function (e) {
+ var currentDate = new Date().toISOString().slice(0, 10);
+ var languageCode = null;
+ ids = [];
+ ids.push($("#selectedLatecome").attr("data-ids"));
+ ids = JSON.parse($("#selectedLatecome").attr("data-ids"));
+ getCurrentLanguageCode(function (code) {
+ languageCode = code;
+ var confirmMessage = excelMessages[languageCode];
+ Swal.fire({
+ text: confirmMessage,
+ icon: "question",
+ showCancelButton: true,
+ confirmButtonColor: "#008000",
+ cancelButtonColor: "#d33",
+ confirmButtonText: "Confirm",
+ }).then(function (result) {
+ if (result.isConfirmed) {
+ $.ajax({
+ type: "GET",
+ url: "/attendance/late-come-early-out-info-export",
+ data: {
+ ids: JSON.stringify(ids),
+ },
+ dataType: "binary",
+ xhrFields: {
+ responseType: "blob",
+ },
+ success: function (response) {
+ const file = new Blob([response], {
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ });
+ const url = URL.createObjectURL(file);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = "Late_come" + currentDate + ".xlsx";
+ document.body.appendChild(link);
+ link.click();
+ },
+ error: function (xhr, textStatus, errorThrown) {
+ console.error("Error downloading file:", errorThrown);
+ },
+ });
+ }
+ });
+ });
+});
+
+// ------------------------------------------------------------------------------------------------------------------------------
+
+// -------------------------------------------------Data Delete Handlers---------------------------------------------------------
+
$("#bulkDelete").click(function (e) {
e.preventDefault();
var languageCode = null;
@@ -814,6 +1075,102 @@ $("#bulkDelete").click(function (e) {
});
});
+$("#hourAccountbulkDelete").click(function (e) {
+ e.preventDefault();
+ var languageCode = null;
+ getCurrentLanguageCode(function (code) {
+ languageCode = code;
+ var confirmMessage = deleteMessages[languageCode];
+ var textMessage = norowdeleteMessages[languageCode];
+ ids = [];
+ ids.push($("#selectedInstances").attr("data-ids"));
+ ids = JSON.parse($("#selectedInstances").attr("data-ids"));
+ if (ids.length === 0) {
+ Swal.fire({
+ text: textMessage,
+ icon: "warning",
+ confirmButtonText: "Close",
+ });
+ } else {
+ Swal.fire({
+ text: confirmMessage,
+ icon: "error",
+ showCancelButton: true,
+ confirmButtonColor: "#008000",
+ cancelButtonColor: "#d33",
+ confirmButtonText: "Confirm",
+ }).then(function (result) {
+ if (result.isConfirmed) {
+ ids = [];
+ ids.push($("#selectedInstances").attr("data-ids"));
+ ids = JSON.parse($("#selectedInstances").attr("data-ids"));
+ $.ajax({
+ type: "POST",
+ url: "/attendance/attendance-account-bulk-delete",
+ data: {
+ csrfmiddlewaretoken: getCookie("csrftoken"),
+ ids: JSON.stringify(ids),
+ },
+ success: function (response, textStatus, jqXHR) {
+ if (jqXHR.status === 200) {
+ location.reload();
+ }
+ },
+ });
+ }
+ });
+ }
+ });
+});
+
+$("#attendanceActivityDelete").click(function (e) {
+ e.preventDefault();
+ var languageCode = null;
+ getCurrentLanguageCode(function (code) {
+ languageCode = code;
+ var confirmMessage = deleteMessages[languageCode];
+ var textMessage = norowdeleteMessages[languageCode];
+ ids = [];
+ ids.push($("#selectedActivity").attr("data-ids"));
+ ids = JSON.parse($("#selectedActivity").attr("data-ids"));
+ if (ids.length === 0) {
+ Swal.fire({
+ text: textMessage,
+ icon: "warning",
+ confirmButtonText: "Close",
+ });
+ } else {
+ Swal.fire({
+ text: confirmMessage,
+ icon: "error",
+ showCancelButton: true,
+ confirmButtonColor: "#008000",
+ cancelButtonColor: "#d33",
+ confirmButtonText: "Confirm",
+ }).then(function (result) {
+ if (result.isConfirmed) {
+ ids = [];
+ ids.push($("#selectedActivity").attr("data-ids"));
+ ids = JSON.parse($("#selectedActivity").attr("data-ids"));
+ $.ajax({
+ type: "POST",
+ url: "/attendance/attendance-activity-bulk-delete",
+ data: {
+ csrfmiddlewaretoken: getCookie("csrftoken"),
+ ids: JSON.stringify(ids),
+ },
+ success: function (response, textStatus, jqXHR) {
+ if (jqXHR.status === 200) {
+ location.reload();
+ }
+ },
+ });
+ }
+ });
+ }
+ });
+});
+
$("#lateComeBulkDelete").click(function (e) {
e.preventDefault();
var languageCode = null;
@@ -862,145 +1219,4 @@ $("#lateComeBulkDelete").click(function (e) {
});
});
-$("#exportActivity").click(function (e) {
- var currentDate = new Date().toISOString().slice(0, 10);
- var languageCode = null;
- ids = [];
- ids.push($("#selectedActivity").attr("data-ids"));
- ids = JSON.parse($("#selectedActivity").attr("data-ids"));
- getCurrentLanguageCode(function (code) {
- languageCode = code;
- var confirmMessage = excelMessages[languageCode];
- Swal.fire({
- text: confirmMessage,
- icon: "question",
- showCancelButton: true,
- confirmButtonColor: "#008000",
- cancelButtonColor: "#d33",
- confirmButtonText: "Confirm",
- }).then(function (result) {
- if (result.isConfirmed) {
- $.ajax({
- type: "GET",
- url: "/attendance/attendance-activity-info-export",
- data: {
- ids: JSON.stringify(ids),
- },
- dataType: "binary",
- xhrFields: {
- responseType: "blob",
- },
- success: function (response) {
- const file = new Blob([response], {
- type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
- });
- const url = URL.createObjectURL(file);
- const link = document.createElement("a");
- link.href = url;
- link.download = "Attendance_activity" + currentDate + ".xlsx";
- document.body.appendChild(link);
- link.click();
- },
- error: function (xhr, textStatus, errorThrown) {
- console.error("Error downloading file:", errorThrown);
- },
- });
- }
- });
- });
-});
-$("#attendanceActivityDelete").click(function (e) {
- e.preventDefault();
- var languageCode = null;
- getCurrentLanguageCode(function (code) {
- languageCode = code;
- var confirmMessage = deleteMessages[languageCode];
- var textMessage = norowdeleteMessages[languageCode];
- ids = [];
- ids.push($("#selectedActivity").attr("data-ids"));
- ids = JSON.parse($("#selectedActivity").attr("data-ids"));
- if (ids.length === 0) {
- Swal.fire({
- text: textMessage,
- icon: "warning",
- confirmButtonText: "Close",
- });
- } else {
- Swal.fire({
- text: confirmMessage,
- icon: "error",
- showCancelButton: true,
- confirmButtonColor: "#008000",
- cancelButtonColor: "#d33",
- confirmButtonText: "Confirm",
- }).then(function (result) {
- if (result.isConfirmed) {
- ids = [];
- ids.push($("#selectedActivity").attr("data-ids"));
- ids = JSON.parse($("#selectedActivity").attr("data-ids"));
- $.ajax({
- type: "POST",
- url: "/attendance/attendance-activity-bulk-delete",
- data: {
- csrfmiddlewaretoken: getCookie("csrftoken"),
- ids: JSON.stringify(ids),
- },
- success: function (response, textStatus, jqXHR) {
- if (jqXHR.status === 200) {
- location.reload();
- }
- },
- });
- }
- });
- }
- });
-});
-
-$("#hourAccountbulkDelete").click(function (e) {
- e.preventDefault();
- var languageCode = null;
- getCurrentLanguageCode(function (code) {
- languageCode = code;
- var confirmMessage = deleteMessages[languageCode];
- var textMessage = norowdeleteMessages[languageCode];
- ids = [];
- ids.push($("#selectedInstances").attr("data-ids"));
- ids = JSON.parse($("#selectedInstances").attr("data-ids"));
- if (ids.length === 0) {
- Swal.fire({
- text: textMessage,
- icon: "warning",
- confirmButtonText: "Close",
- });
- } else {
- Swal.fire({
- text: confirmMessage,
- icon: "error",
- showCancelButton: true,
- confirmButtonColor: "#008000",
- cancelButtonColor: "#d33",
- confirmButtonText: "Confirm",
- }).then(function (result) {
- if (result.isConfirmed) {
- ids = [];
- ids.push($("#selectedInstances").attr("data-ids"));
- ids = JSON.parse($("#selectedInstances").attr("data-ids"));
- $.ajax({
- type: "POST",
- url: "/attendance/attendance-account-bulk-delete",
- data: {
- csrfmiddlewaretoken: getCookie("csrftoken"),
- ids: JSON.stringify(ids),
- },
- success: function (response, textStatus, jqXHR) {
- if (jqXHR.status === 200) {
- location.reload();
- }
- },
- });
- }
- });
- }
- });
-});
+// ------------------------------------------------------------------------------------------------------------------------------
diff --git a/attendance/templates/attendance/late_come_early_out/report_list.html b/attendance/templates/attendance/late_come_early_out/report_list.html
index d0fb08f4a..463005cf5 100644
--- a/attendance/templates/attendance/late_come_early_out/report_list.html
+++ b/attendance/templates/attendance/late_come_early_out/report_list.html
@@ -118,169 +118,3 @@
-
-
diff --git a/attendance/templates/attendance/late_come_early_out/reports.html b/attendance/templates/attendance/late_come_early_out/reports.html
index c9abef564..8956be97e 100644
--- a/attendance/templates/attendance/late_come_early_out/reports.html
+++ b/attendance/templates/attendance/late_come_early_out/reports.html
@@ -23,5 +23,4 @@
{% include 'attendance/late_come_early_out/report_list.html' %}
-
{% endblock %}
diff --git a/attendance/views/views.py b/attendance/views/views.py
index 4d59d34cf..1d9c48371 100644
--- a/attendance/views/views.py
+++ b/attendance/views/views.py
@@ -299,21 +299,28 @@ def attendance_view(request):
else:
template = "attendance/attendance/attendance_empty.html"
validate_attendances_ids = json.dumps(
- list(validate_attendances.values_list("id", flat=True))
+ [
+ instance.id
+ for instance in paginator_qry(
+ validate_attendances, request.GET.get("vpage")
+ ).object_list
+ ]
)
ot_attendances_ids = json.dumps(
- list(
- paginator_qry(
+ [
+ instance.id
+ for instance in paginator_qry(
ot_attendances, request.GET.get("opage")
- ).object_list.values_list("id", flat=True)
- )
+ ).object_list
+ ]
)
attendances_ids = json.dumps(
- list(
- paginator_qry(attendances, request.GET.get("page")).object_list.values_list(
- "id", flat=True
- )
- )
+ [
+ instance.id
+ for instance in paginator_qry(
+ attendances, request.GET.get("page")
+ ).object_list
+ ]
)
return render(
request,
@@ -492,7 +499,12 @@ def view_my_attendance(request):
else:
template = "attendance/own_attendance/own_empty.html"
attendances_ids = json.dumps(
- list(employee_attendances.values_list("id", flat=True))
+ [
+ instance.id
+ for instance in paginator_qry(
+ employee_attendances, request.GET.get("page")
+ ).object_list
+ ]
)
return render(
request,
@@ -535,19 +547,20 @@ def attendance_overtime_view(request):
This method is used to view attendance account or overtime account.
"""
previous_data = request.GET.urlencode()
- accounts = AttendanceOverTime.objects.all()
- if accounts.exists():
+ filter_obj = AttendanceOverTimeFilter(request.GET)
+ if filter_obj.qs.exists():
template = "attendance/attendance_account/attendance_overtime_view.html"
else:
template = "attendance/attendance_account/overtime_empty.html"
accounts = filtersubordinates(
- request, accounts, "attendance.view_attendanceovertime"
+ request, filter_obj.qs, "attendance.view_attendanceovertime"
)
form = AttendanceOverTimeForm()
form = choosesubordinates(request, form, "attendance.add_attendanceovertime")
- filter_obj = AttendanceOverTimeFilter()
export_obj = AttendanceOverTimeFilter()
export_fields = AttendanceOverTimeExportForm()
+ data_dict = parse_qs(previous_data)
+ get_key_instances(AttendanceOverTime, data_dict)
return render(
request,
template,
@@ -559,6 +572,7 @@ def attendance_overtime_view(request):
"export_obj": export_obj,
"export_fields": export_fields,
"gp_fields": AttendanceOvertimeReGroup.fields,
+ "filter_dict": data_dict,
},
)
@@ -965,12 +979,12 @@ def late_come_early_out_bulk_delete(request):
ids = json.loads(ids)
for attendance_id in ids:
try:
- latecome = AttendanceLateComeEarlyOut.objects.get(id=attendance_id)
- latecome.delete()
+ late_come = AttendanceLateComeEarlyOut.objects.get(id=attendance_id)
+ late_come.delete()
messages.success(
request,
_("{employee} Late-in early-out deleted.").format(
- employee=latecome.employee_id
+ employee=late_come.employee_id
),
)
except AttendanceLateComeEarlyOut.DoesNotExist:
@@ -986,27 +1000,53 @@ def late_come_early_out_export(request):
This view function takes a GET request and exports attendance late come early out data into an Excel file.
The exported Excel file will include the selected fields from the AttendanceLateComeEarlyOut model.
"""
+ late_come_data = {}
+ selected_columns = []
+ form = LateComeEarlyOutExportForm()
today_date = date.today().strftime("%Y-%m-%d")
file_name = f"Late_come_{today_date}.xlsx"
attendances = LateComeEarlyOutFilter(request.GET).qs
selected_fields = request.GET.getlist("selected_fields")
- model_fields = AttendanceLateComeEarlyOut._meta.get_fields()
- lateCome_data = {}
- for field in model_fields:
- field_name = field.name
- if field_name in selected_fields:
- lateCome_data[field.verbose_name] = []
- for attendance in attendances:
- value = getattr(attendance, field_name)
- if value is True:
- value = "Yes"
- elif value is False:
- value = "No"
- lateCome_data[field.verbose_name].append(value)
- data_frame = pd.DataFrame(data=lateCome_data)
+
+ if not selected_fields:
+ selected_fields = form.fields["selected_fields"].initial
+ ids = request.GET.get("ids")
+ id_list = json.loads(ids)
+ attendances = AttendanceLateComeEarlyOut.objects.filter(id__in=id_list)
+
+ for field in form.fields["selected_fields"].choices:
+ value = field[0]
+ key = field[1]
+ if value in selected_fields:
+ selected_columns.append((value, key))
+
+ for column_value, column_name in selected_columns:
+ nested_attributes = column_value.split("__")
+ late_come_data[column_name] = []
+ for attendance in attendances:
+ value = attendance
+ for attr in nested_attributes:
+ value = getattr(value, attr, None)
+ if value is None:
+ break
+ data = str(value) if value is not None else ""
+ if data == "True":
+ data = _("Yes")
+ elif data == "False":
+ data = _("No")
+ late_come_data[column_name].append(data)
+
+ data_frame = pd.DataFrame(data=late_come_data)
+ data_frame = data_frame.style.applymap(
+ lambda x: "text-align: center", subset=pd.IndexSlice[:, :]
+ )
response = HttpResponse(content_type="application/ms-excel")
response["Content-Disposition"] = f'attachment; filename="{file_name}"'
- data_frame.to_excel(response, index=False)
+ writer = pd.ExcelWriter(response, engine="xlsxwriter")
+ data_frame.to_excel(writer, index=False, sheet_name="Sheet1")
+ worksheet = writer.sheets["Sheet1"]
+ worksheet.set_column("A:Z", 20)
+ writer.close()
return response