I am trying to do something which is very complicated to me.... I am trying to create an invoice with two objects 1) Invoice 2) InvoiceItems (Line Items)
The same activity is used to edit existing or to create a new invoice hence both Invoice and InvoiceItems are nullable.
The way I am trying to make this work is when the activity is launched,
The issues are two fold:
Please have a look at the code below:
class EditInvoice : AppCompatActivity() {
companion object {
@JvmStatic
fun start(context: Context, invoice: Invoice?, invoiceItems: InvoiceItems?) {
val starter = Intent(context, EditInvoice::class.java)
.putExtra("invoice", invoice)
.putExtra("invoiceItems", invoiceItems)
context.startActivity(starter)
}
}
private var invoice: Invoice? = null
private var invoiceEdit: Invoice? = null
private lateinit var contact: Contact
private var invoiceItems: List<InvoiceItems>? = null
private lateinit var dueDate: Calendar
private val calendar = Calendar.getInstance()
private var total = 0
private var subTotal = 0
private var taxrate = 0
private var invoiceType: String = ""
private var invoiceUpdt: InvoiceItems? = null
private var j: Int = 0
private var clientLkey: String? = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_invoice)
val toolbar: Toolbar = findViewById(R.id.toolbar_editinv)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowHomeEnabled(true)
val invoiceClient = findViewById<AutoCompleteTextView>(R.id.invoiceClient)
val invoiceDueDt = findViewById<TextView>(R.id.invoiceDueDt)
dueDate = Calendar.getInstance()
//getting values from intent
invoice = intent.getSerializableExtra("invoice") as? Invoice
invoiceItems = intent.getSerializableExtra("invoiceItem") as? List<InvoiceItems>
invoiceUpdt = intent.getSerializableExtra("invoiceItemUpdt") as? InvoiceItems
j = intent.getIntExtra("i",0)
if (invoice == null){
invoiceType = "new"
edit_inv.text = "Add Invoice"
invoiceEdit = Invoice("0","0","0","0","0","0",0,0,null,"")
addinvoiceItem()
} else {
invoiceEdit = invoice
editInvoice()
}
//Setup Due date for the invoice
invoiceDueDt.setOnClickListener {
showCalendar()
}
//Auto complete based on database for selecting the client
val clientContact: List<Contact> = ArrayList<Contact>()
val adapter = ClientSelectAdapter(this, R.layout.userlatomcontacts, clientContact)
invoiceClient.setAdapter(adapter)
invoiceClient.threshold = 2
invoiceClient.setOnItemClickListener { parent, _, position, id ->
val selectedClient = parent.adapter.getItem(position) as Contact?
invoiceClient.setText(selectedClient?.name)
clientLkey = selectedClient?.lookupKey
}
val saveInvoice = findViewById<TextView>(R.id.editinv_save)
val invoiceTitle = findViewById<EditText>(R.id.invoiceTitle)
saveInvoice.setOnClickListener {
if(invoiceTitle.toString().isEmpty()){
Toast.makeText(this, "Invoice title can't be empty", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if(invoiceClient.toString().isEmpty()){
Toast.makeText(this, "Please select a Client for the invoice", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if(invoiceDueDt.toString().isEmpty()){
Toast.makeText(
this,
"Please enter the due date for the invoice",
Toast.LENGTH_SHORT
).show()
return@setOnClickListener
}
if(invoiceItems == null){
Toast.makeText(
this,
"Please enter the line items/services for the invoice",
Toast.LENGTH_SHORT
).show()
return@setOnClickListener
}
//updating values as current
updateInvoiceValues()
//Storing values to DB
val db = AppDatabase.getDatabase(this)
if(invoiceType == "new"){
db.InvoicesDao().addInvoice(invoice!!)
for(i in invoiceItems!!.indices){
db.InvoiceItemsDao().addInvItem(invoiceItems!![i])
}
} else {
db.InvoicesDao().updateInvoice(invoice!!)
for(i in invoiceItems!!.indices){
db.InvoiceItemsDao().updateInvItem(invoiceItems!![i])
}
}
}
}
inner class ClientSelectAdapter(
context: Context,
@LayoutRes private val layoutResource: Int,
private var allContacts: List<Contact>
):
ArrayAdapter<Contact>(context, layoutResource, allContacts),
Filterable {private var mContact: List<Contact> = allContacts
override fun getCount(): Int {
return mContact.size
}
override fun getItem(p0: Int): Contact {
return mContact[p0]
}
override fun getItemId(p0: Int): Long {
// Or just return p0
return mContact[p0].id.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var view = convertView
if (view == null) {
view = LayoutInflater.from(parent.context)
.inflate(layoutResource, parent, false)
}
val invoiceClient = view!!.findViewById<View>(R.id.invClientName) as TextView
invoiceClient.text = mContact[position].name
val clientProfile = view.findViewById<View>(R.id.profile_image) as ShapeableImageView
Picasso.get().load(mContact[position].photoUri)
.placeholder(R.drawable.ic_baseline_whatshot_24).fit().centerCrop()
.into(clientProfile)
val contLabel = view.findViewById<View>(R.id.salesLabelText) as TextView
if(mContact[position].label != null){
contLabel.text = mContact[position].label
if (mContact[position].label == "Lead" || mContact[position].label == "LEAD"){
contLabel.setBackgroundColor(resources.getColor(R.color.ColorPurple))
} else if (mContact[position].label == "Qualified"){
contLabel.setBackgroundColor(resources.getColor(R.color.ColorBlueNote))
} else if (mContact[position].label== "Proposal"){
contLabel.setBackgroundColor(resources.getColor(R.color.ColorMaroon))
} else if (mContact[position].label == "Client"){
contLabel.setBackgroundColor(resources.getColor(R.color.ColorGreen))
} else if (mContact[position].label == "Invoiced"){
contLabel.setBackgroundColor(resources.getColor(R.color.ColorYellow))
} else if (mContact[position].label == "Unpaid"){
contLabel.setBackgroundColor(resources.getColor(R.color.ColorRed))
} else if (mContact[position].label == ""){
salesLabel.visibility = View.GONE
}
}
return view
}
override fun getFilter(): Filter {
return object : Filter() {
override fun publishResults(
charSequence: CharSequence?,
filterResults: FilterResults
) {
mContact = filterResults.values as List<Contact>
notifyDataSetChanged()
}
override fun performFiltering(charSequence: CharSequence?): FilterResults {
var queryString = charSequence?.toString()?.toLowerCase(Locale.ROOT)
val results = FilterResults()
results.values = if (queryString == null || queryString.isEmpty())
allContacts
else {
queryString = "%${charSequence}%"
val db = AppDatabase.getDatabase(context)
allContacts = db.contactsDao().getBySearch(queryString)
allContacts
}
return results
}
}
}
}
private fun showCalendar() {
val datePicker = DatePickerDialog(
this,
R.style.DateTimePickerTheme,
{ datePicker: DatePicker, year: Int, month: Int, day: Int ->
dueDate.set(year, month, day)
updateDateFields()
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
)
datePicker.show()
}
private fun updateDateFields() {
val formatter = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault())
val invoiceDueDt = findViewById<TextView>(R.id.invoiceDueDt)
if(dueDate.time < Calendar.getInstance().time)
{
Toast.makeText(this, "Please select a date that is today or later", Toast.LENGTH_SHORT).show()
} else {
invoiceDueDt.text = formatter.format(dueDate.time)
}
}
private fun editInvoice() {
val invoiceTitle = findViewById<EditText>(R.id.invoiceTitle)
val invoiceClient = findViewById<AutoCompleteTextView>(R.id.invoiceClient)
val itemOtherDetails = findViewById<EditText>(R.id.itemOtherDetails)
if(invoice!!.invoiceTitle != "0"){
invoiceTitle.setText(invoice!!.invoiceTitle)
}
if(invoice!!.invoiceClientLKey != "0"){
//getting client name from the database
val getClient = AppDatabase.getDatabase(this)
contact = getClient.contactsDao().getSingleContact(invoice!!.invoiceClientLKey!!)
invoiceClient.setText(contact.name)
}
if(invoice!!.othComments != "0"){
itemOtherDetails.setText(invoice!!.othComments)
}
addinvoiceItem()
invoiceClient.setOnClickListener{
val intent = Intent(this, ContactDetailsHome::class.java)
intent.putExtra("contact", contact as Serializable)
this.startActivity(intent)
}
}
private fun addinvoiceItem() {
val invoiceItemsLayout = findViewById<RelativeLayout>(R.id.invoiceItemsLayout)
val invoiceSubValue = findViewById<TextView>(R.id.invoiceSubValue)
val invoiceTaxValue = findViewById<TextView>(R.id.invoiceTaxValue)
val invoiceTotalValue = findViewById<TextView>(R.id.invoiceTotalValue)
if(invoice != null) {
if(intent.getSerializableExtra("invoiceItem") == null) {
//getting invoice items stored in the local db
val getItems = AppDatabase.getDatabase(this)
invoiceItems = getItems.InvoiceItemsDao().getinvItem(invoice!!.invNo)
}
for (i in invoiceItems!!.indices+1) {
//Check if invoiceItems have been received from EditBillingItems if yes, then update the particular object
if(j == i){
invoiceItems!![i].itemTitle = invoiceUpdt!!.itemTitle
invoiceItems!![i].itemDesc = invoiceUpdt!!.itemDesc
invoiceItems!![i].itemQty = invoiceUpdt!!.itemQty
invoiceItems!![i].itemRate = invoiceUpdt!!.itemRate
invoiceItems!![i].itemTaxable = invoiceUpdt!!.itemTaxable
invoiceItems!![i].itemTotal = invoiceUpdt!!.itemTotal
}
//Item Name Display on Invoice Activity
val itemTitle: ArrayList<TextView>? = null
itemTitle!![i] = TextView(this)
val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
) // or wrap_content
layoutParams.setMargins(0, 0, 0, 0)
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START)
itemTitle[i].hint = "Enter Item Title"
itemTitle[i].textSize = 16f
itemTitle[i].layoutParams = layoutParams
itemTitle[i].setTextColor(resources.getColor(R.color.txtcolor))
invoiceItemsLayout.addView(itemTitle[i])
itemTitle[i].text = invoiceItems!![i].itemTitle
//Total Item Value
val itemValue: ArrayList<TextView>? = null
itemValue!![i] = TextView(this)
val layoutParams2: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
) // or wrap_content
layoutParams2.setMargins(0, 0, 0, 0)
layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_END)
itemValue[i].text = "00.00"
itemValue[i].textSize = 16f
itemValue[i].setTextColor(resources.getColor(R.color.txtcolor))
itemValue[i].setPadding(10, 0, 0, 10)
itemValue[i].layoutParams = layoutParams2
invoiceItemsLayout.addView(itemValue[i], layoutParams)
itemValue[i].text = invoiceItems!![i].itemTotal.toString()
if(invoiceItems!![i + 1].itemTitle == ""){
//New Item Title field
val itemTitle: ArrayList<TextView>? = null
itemTitle!![i] = TextView(this)
val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
) // or wrap_content
layoutParams.setMargins(0, 0, 0, 0)
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START)
itemTitle[i].hint = "Enter Item Title"
itemTitle[i].textSize = 16f
itemTitle[i].setTextColor(resources.getColor(R.color.txtcolor))
invoiceItemsLayout.addView(itemTitle[i], layoutParams)
//New item value field
val itemValue: ArrayList<TextView>? = null
itemValue!![i] = TextView(this)
val layoutParams2: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
) // or wrap_content
layoutParams2.setMargins(0, 0, 0, 0)
layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_END)
itemValue[i].text = "00.00"
itemValue[i].textSize = 16f
itemValue[i].setTextColor(resources.getColor(R.color.txtcolor))
itemValue[i].setPadding(10, 0, 0, 10)
itemValue[i].layoutParams = layoutParams2
invoiceItemsLayout.addView(itemValue[i], layoutParams)
itemTitle[i].setOnClickListener {
val invoiceItemSend: InvoiceItems = invoiceItems!![i]
updateInvoiceValues()
val intent = Intent(this, EditBillingItem::class.java)
intent.putExtra("invoiceItem", invoiceItems as Serializable)
intent.putExtra("index", i)
intent.putExtra("invoice", invoice as Serializable)
this.startActivity(intent)
}
}
subTotal += invoiceItems!![i].itemTotal
val taxValue = invoiceItems!![i].itemRate!!
taxrate += (subTotal * taxValue)
}
invoiceSubValue.setText(subTotal)
total = subTotal + taxrate
invoiceTaxValue.setText(taxrate)
invoiceTotalValue.setText(total)
} else {
//Item Name Display on Invoice Activity
val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
) // or wrap_content
layoutParams.setMargins(0, 0, 0, 0)
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START)
val itemTitle = TextView(this)
itemTitle.hint = "Enter Item Title"
itemTitle.textSize = 16f
itemTitle.setTextColor(resources.getColor(R.color.txtcolor))
invoiceItemsLayout.addView(itemTitle, layoutParams)
//Total Item Value
val itemValue = TextView(this)
val layoutParams2: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
) // or wrap_content
layoutParams2.setMargins(0, 0, 0, 0)
layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_END)
itemValue.text = "00.00"
itemValue.textSize = 16f
itemValue.setTextColor(resources.getColor(R.color.txtcolor))
itemValue.setPadding(10, 0, 0, 10)
itemValue.layoutParams = layoutParams2
invoiceItemsLayout.addView(itemValue, layoutParams2)
itemTitle.setOnClickListener {
updateInvoiceValues()
val intent = Intent(this, EditBillingItem::class.java)
if(invoiceItems != null){
intent.putExtra("invoiceItem", invoiceItems as Serializable)
}
if(invoice != null){
intent.putExtra("invoice", invoice as Serializable)
}
intent.putExtra("index",0)
this.startActivity(intent)
}
}
}
private fun updateInvoiceValues() {
//updating values
val invoiceTitle = findViewById<EditText>(R.id.invoiceTitle)
val invoiceDueDt = findViewById<TextView>(R.id.invoiceDueDt)
if(invoiceTitle.text.toString().isNotEmpty()){
invoiceEdit?.invoiceTitle = invoiceTitle.text.toString()
}
if(clientLkey != ""){
invoiceEdit?.invoiceClientLKey = clientLkey
}
if (invoiceDueDt.text.toString().isNotEmpty()){
invoiceEdit?.dueDate = invoiceDueDt.text.toString()
}
}
ACTIVTY B - edit Item
class EditBillingItem : AppCompatActivity() {
companion object {
@JvmStatic
fun start(context: Context, invoice: Invoice?, invoiceItems: InvoiceItems?) {
val i = 0
val starter = Intent(context, EditInvoice::class.java)
.putExtra("invoice", invoice)
.putExtra("invoiceItem", invoiceItems)
.putExtra("i", i)
context.startActivity(starter)
}
}
private var invoiceItem: List<InvoiceItems>? = null
private var invoice: Invoice? = null
private lateinit var invoiceItemUpdt: InvoiceItems
private var i = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_billing_item_update)
invoice = intent.getSerializableExtra("invoiceEdit") as Invoice
invoiceItem = intent.getSerializableExtra("invoiceItem") as? List<InvoiceItems>
i = intent.getIntExtra("i", i)
val itemTitle = findViewById<EditText>(R.id.itemTitle)
val itemDesc = findViewById<EditText>(R.id.itemDesc)
val itemQty = findViewById<EditText>(R.id.itemQty)
val itemChrgRt = findViewById<EditText>(R.id.itemChrgRt)
val itemTaxable = findViewById<SwitchCompat>(R.id.itemTaxable)
val itemTotal = findViewById<TextView>(R.id.itemTotal)
val itemBack = findViewById<ImageButton>(R.id.itemBack)
if(intent.getSerializableExtra("invoiceItems") != null){
itemTitle.setText(invoiceItem!![i].itemTitle)
itemDesc.setText(invoiceItem!![i].itemDesc)
itemQty.setText(invoiceItem!![i].itemQty.toString())
itemChrgRt.setText(invoiceItem!![i].itemRate.toString())
itemTaxable.isChecked = invoiceItem!![i].itemTaxable == true
itemTotal.setText(invoiceItem!![i].itemTotal)
}
itemChrgRt.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int,
count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
if(s.toString().isNotEmpty() && itemQty.text.toString().isNotEmpty()){
var i = itemQty.text.toString().toInt()
val j = s.toString().toInt()
i *= j
itemTotal.text = i.toString()
}
}
})
itemQty.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int,
count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
if(itemChrgRt.text.toString().isNotEmpty() && itemChrgRt.text.toString().isNotEmpty()){
var i = s.toString().toInt()
val j = itemChrgRt.text.toString().toInt()
i *= j
itemTotal.text = i.toString()
}
}
})
itemBack.setOnClickListener {
if(itemTitle.toString().isBlank() && itemQty.toString().isNotBlank() || itemChrgRt.toString().isNotBlank() && itemTitle.toString().isBlank()){
Toast.makeText(this, "Title can't be empty", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
invoiceItemUpdt = InvoiceItems("0","0","0",null,null,null,null,0)
invoiceItemUpdt.itemTitle = itemTitle.toString()
invoiceItemUpdt.itemTitle = itemDesc.toString()
if(itemQty.toString().isEmpty()){
invoiceItemUpdt.itemQty = 1
} else {
invoiceItemUpdt.itemQty = itemQty.text.toString().toInt()
}
invoiceItemUpdt.itemRate = itemChrgRt.text.toString().toInt()
itemTaxable.isChecked = invoiceItemUpdt.itemTaxable == true
val intent = Intent(this, EditInvoice::class.java)
intent.putExtra("invoice", invoice as Serializable)
intent.putExtra("invoiceItem", invoiceItem as Serializable)
intent.putExtra("invoiceItemUpdt", invoiceItemUpdt as Serializable)
intent.putExtra("index", i)
this.startActivity(intent)
}
}
Error from logcat java.lang.NullPointerException: null cannot be cast to non-null type java.io.Serializable at in.latom.latom.Billing.ui.EditInvoice$addinvoiceItem$2.onClick(EditInvoice.kt:449) at android.view.View.performClick(View.java:7398) at android.view.View.performClickInternal(View.java:7375) at android.view.View.access$3700(View.java:817) at android.view.View$PerformClick.run(View.java:28516) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7858) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:984)
Your invoice
variable will be null if this line returns null:
invoice = intent.getSerializableExtra("invoice") as? Invoice
Later in your code, you had never populated your invoice
variable, but you force unwraps it in this line:
db.InvoicesDao().addInvoice(invoice!!)
This if statement always be null:
if(invoice != null){
intent.putExtra("invoice", invoice as Serializable)
}